"자바 엔터프라이즈 개발을 편하게 해주는 오픈소스 경량급 애플리케이션 프레임워크"
디자인 패턴은 객체 지향의 특성 중 상속(extends), 인터페이스(interface/implements), 합성(객체를 속성으로 사용)을 이용한다.
Adapter는 변환기라 할 수 있고 변환기는 서로 다른 두 인터페이스 사이에 통신이 가능하도록 해준다.
JDBC가 어댑터 패턴을 이용해 다양한 데이터베이스 시스템을 단일한 인터페이스로 조작할 수 있게 해주는 예이다.
또한 JRE도 마찬가지로 어댑터의 역할을 한다.
Adapter Pattern은 객체를 속성으로 만들어서 참조하는 디자인 패턴
➡️ 호출당하는 쪽의 메서드를 호출하는 쪽의 코드에 대응하도록 중간에 Adapter를 통해 호출
public class A {
void tellnameA() {
// ...
}
}
public class B {
void tellnameB() {
//...
}
}
비슷한 수행을 하는 메서드를 위와 같이 따로 만들어 사용하지 않고
아래와 같이 메서드명을 통일하여 tellnameA,B 에 대한 변환기를 만든다
public class A {
void tellname() {
// ...
}
}
public class B {
void tellname() {
//...
}
}
main() {
A a = new A();
B b = new B();
a.tellname();
b.tellname();
}
제어 흐름을 조정하기 위한 목적으로 중간에 대리자를 두는 패턴 (프록시는 대리자라는 뜻)
OCP(개방 폐쇄 원칙), DIP(의존 역전 원칙)이 적용된 디자인 패턴
인터페이스를 사용하면 서비스 객체가 들어갈 자리에 대리자 객체를 투입해 클라이언트 쪽에서는 실제 서비스 객체를 통해 메서드를 호출하고 반환값을 받는지, 대리자 객체를 통해 메서드를 호출하고 반환값을 받는지 알 수 없게 처리할 수 있다.
package 프록시
public interface DoSomething {
String Something();
}
package 프록시
public class Do implements DoSomething {
public String Something() {
return "썸띵";
}
}
package 프록시
public class Proxy implements DoSomething {
DoSomething doSomething;
public String Something() {
System.out.println("호출에 대한 흐름 제어가 주목적, 반환 결과를 그대로 전달");
doSomething = new Do();
return doSomething.Something();
}
}
package 프록시
public class Client {
public static void main(String[] args) {
//프록시를 이용한 호출
doSomething proxy = new Proxy();
System.out.println(proxy.Something());
}
}
메서드 호출의 반환값에 변화를 주기 위해 중간에 장식자를 두는 패턴
구현은 프록시 패턴과 같음
OCP(개방 폐쇄 원칙), DIP(의존 역전 원칙)이 적용된 디자인 패턴
프록시 패턴 | 데코레이터 패턴 |
---|---|
제어 흐름 변경 혹은 별도의 로직 처리를 목적으로 함 클라이언트가 받는 반환값을 특별한 경우가 아니면 변경하지 않음 | 클라이언트가 받는 반환값에 장식을 더함 |
package 데코레이터
public class Decorator implements DoSomething {
DoSomething doSomething;
public String Something() {
System.out.println("호출에 대한 장식 주목적, 반환 결과에 장식을 더해 클라이언트에 전달");
doSomething = new Do();
return "장식" + doSomething.Something();
}
}
package 데코레이터
public class Client {
public static void main(String[] args) {
//프록시를 이용한 호출
doSomething decorator = new Decorator();
System.out.println(decorator.Something());
}
}
클래스의 인스턴스(객체)를 하나만 만들어 사용하는 패턴
오직 인스턴스를 하나만 만들고 그것을 계속해서 재사용
싱글턴 패턴을 적용할 경우 의미상 두 개의 객체가 존재해선 안됨
-> 객체 생성을 위한 new에 제약 필요
-> 생성된 단일 객체를 반환할 수 있는 메서드 필요
package 싱글턴
public class Singleton {
static Singleton 정적_참조_변수;
// 생성자에 private 접근 제어자 지정
private Singleton() {};
//객체를 반환하는 메서드
public static Singleton getInstance() {
//정적참조변수에 객체 할당돼있지 않은 경우에 new를 통해 객체 생성
if (정적_참조_변수 ==null) {
정적_참조_변수 = new Singleton();
}
return 정적_참조_변수;
}
}
main() {
Singleton One = Singleton.getInstance();
Singleton Two = Singleton.getInstance();
}
One, Two는 모두 메모리에서 하나의 단일 객체 :Singleton를 참조한다.
단일 객체의 경우 공유 객체로 사용되기에 속성을 갖지 않는다. -> 다른 참조 변수에 영향 끼칠 수 있기 때문
(읽기 전용 속성 or 다른 단일 객체에 대한 참조를 속성으로 갖는 것은 문제되지 않음)
- private 생성자 가짐
- 단일 객체 참조 변수를 정적 속성으로 가짐
- 단일 객체 참조 변수가 참조하는 단일 객체를 반환하는 getInstance() 정적 메서드를 가짐
- 단일 객체는 쓰기 가능한 속성을 갖지 않는 것이 정석
상위 클래스에 공통 로직을 수행하는 Tempate method와 하위 클래스에 오버라이딩을 강제하는 Abstract method 또는 선택적으로 오버라이딩 할 수 있는 Hook method를 두는 패턴 (템블릿-견본)
➡️ 상위 클래스의 견본 메서드에서 하위 클래스가 오버라이딩한 메서드를 호출하는 패턴
템플릿 메서드패턴은 DIP(의존 역전 원칙)을 활용
템플릿 메서드 | 공통 로직을 수행 로직 중에 하위 클래스에서 오버라이딩한 추상 메서드/훅 메서드 호출 |
---|---|
템플릿 메서드에서 호출하는 추상 메서드 | 하위 클래스가 반드시 오버라이딩 해야 함 |
------------------------------- 템플릿 메서드에서 호출하는 훅 메서드 | ---------------------------------------------------------------------------- 하위 클래스가 선택적으로 오버라이딩 함 |
public abstract class 동물 {
//Template method
public void 템플릿메서드() {
//...
}
//Abstract method
abstract void 추상메서드();
//Hook method
void 훅메서드() {
//...
}
}
public class 판다 extends 동물 {
@Override
//Abstract method 오버라이딩
void 추상메서드() {
//...
}
@Override
//Hook method 오버라이딩
void 훅메서드() {
//...
}
}
객체지향에서 팩토리는 객체를 생성한다
➡️ 팩토리 메서드는 객체를 생성하는 메서드
➡️ 팩토리 메서드 패턴은 하위 클래스에서 팩터리 메서드를 오버라이딩해서 객체를 반환하는 것
오버라이드된 메서드가 객체를 반환하는 패턴
DIP(의존 역전 원칙)을 활용
디자인 패턴의 꽃이라 불리는 전략 패턴
클라이언트가 전략을 생성해 전략을 실행할 컨텍스트에 주입하는 패턴
OCP(개방 폐쇄 원칙), DIP(의존 역전 원칙)이 적용된 패턴
클라이언트는 다양한 전략 중 하나를 선택해 생성한 후 컨텍스트에 주입
1. 클라이언트 ----전략 객체 생성----> 전략
2. 클라이언트 ----전략 객체 주입----> 컨텍스트
예시)
사육사가 음식을 판다에게 급여하면 판다는 제공받은 음식에 따라 식사를 한다.
전략 - 음식 / 컨텍스트 - 판다 / 클라이언트(제 3자) - 사육사
//인터페이스
package 전략패턴
public interface Strategy {
public abstract void runStrategy();
}
//전략
package 전략패턴
public class Strategy대나무 implements Strategy {
@Override
public void runStrategy() {
System.out.println("와그작");
}
}
package 전략패턴
public class Strategy딸기 implement Strategy {
@Override
public void runStrategy() {
System.out.println("딸기는 안먹어");
}
}
//컨텍스트
pacakge 전략패턴
public class 판다 {
void runContext(Strategy strategy) {
System.out.println("밥 시간");
strategy.runStrategy();
System.out.println("노는 시간");
}
}
//클라이언트
pacakge 전략패턴
public clss Client {
public static void main(String[] args) {
Strategy strategy = null;
판다 푸바오 = new 판다();
// 푸바오에게 대나무를 준다.
strategy = new Strategy대나무();
푸바오.runContext(strategy);
// 푸바오에게 딸기를 준다.
strategy = new Strategy딸기();
푸바오.runContext(strategy);
//실행결과
밥 시간
와그작
노는 시간
밥시간
딸기는 안먹어
노는 시간
템플릿 메서트 패던 - 상속 이용
전략 패턴 - 객체 주입 이용단일 상속만이 가능한 자바에서는 상속이라는 제한이 있는 템플릿 메서드 패턴보다는 전략 패턴을 많이 활용
전략 패턴의 변형
스프링의 3대 프로그래밍 모델 중 하나인 DI(의존성 주입)에서 사용하는 특별한 형태의 전략 패턴
전략을 일명 내부 클래스로 구현한 전략 패턴
OCP(개방 폐쇄 원칙), DIP(의존 역전 원칙)이 적용된 패턴
전략 패턴과 모두 동일하나 전략을 익명 내부 클래스로 정의해서 사용하는 차이가 있음
중복되는 부분을 컨텍스트로 이관하여 코드가 간결해짐
➡️ 스프링은 리팩터링된 템플릿 콜백 패턴을 DI에 적극 활용
//인터페이스
package 템플릿콜백패턴
public interface Strategy {
public abstract void runStrategy();
}
//컨텍스트
pacakge 템플릿콜백패턴
public class 판다 {
void runContext(Strategy strategy) {
System.out.println("밥 시간");
strategy.runStrategy();
System.out.println("노는 시간");
}
//전략 생성
private Strategy Eat(final String voice) {
return new Strategy() {
@Override
public void runStrategy() {
System.out.println(voice);
}
};
}
}
//클라이언트
pacakge 템플릿콜백패턴
public clss Client {
public static void main(String[] args) {
Strategy strategy = null;
판다 푸바오 = new 판다();
푸바오.runContext("와그작");
푸바오.runContext("딸기는 안먹어");
김종민, '스프링 입문을 위한 자바 객체 지향의 원리와 이해', 위키북스 참고