변하지 않는 부분을 Context
에, 변하는 부분을 Strategy
라는 인터페이스를 구현하도록, 즉, 상속이 아닌 위임을 통해 변하지 않는 부분과 변하는 부분을 분리한다.
Context
는 Strategy
인터페이스에만 의존하기 때문에 Strategy
의 구현체를 변경해도 코드를 변경하지 않아도 된다. (반대로 Context
가 변경되도 Strategy
의 코드에 영향을 주지 않는다.)
스프링에서 의존관계 주입에 사용하는 것이 전략패턴이다.
전략 패턴의 목적
알고리즘의 제품군을 정의하고 각각의 캡슐화하여(Strategy
, 인터페이스) 상호교환이 가능하게 만든다. 전략을 사용하면 알고리즘을 사용하는 클라이언트와 독립적으로 알고리즘을 변경할 수 있다.
public class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public execute() {
long startTime = 시작시간_가져온다();
strategy.call();
long endTime = 종료시간_가져온다();
print(endTime - startTime);
}
}
Context
클래스를 정의한다.
public interface Strategy {
void call();
}
Strategy
인터페이스를 정의한다.
public class SubjectStrategy1 implements Strategy {
@Override
void call() {
핵심 기능 구현();
}
}
public class SubjectStrategy2 implements Strategy {
@Override
void call() {
핵심 기능 구현();
}
}
전략을 구현한다.
void run() {
Context context1 = new Context(new SubjectStrategy1());
Context context2 = new Context(new SubjectStrategy2());
context1.execute();
context2.execute();
}
위 방식은 "선 조립 후 실행" 방식이다. 스프링에서 어플리케이션의 로딩 시점에 의존관계를 주입한 후에 실제 요청을 처리하는 원리이다.
단점
조립 이후에는 전략을 변경하기 쉽지 않다. setter
를 통해 변경하면 되지만, 싱글톤일 경우 동시성 이슈 등을 고려해야 한다.
public class Context {
public execute(Strategy strategy) {
long startTime = 시작시간_가져온다();
strategy.call();
long endTime = 종료시간_가져온다();
print(endTime - startTime);
}
}
인자를 통해 전략을 넘겨 받으면 실행할 때 마다 인수를 전달한다.
컨텍스트를 새로 생성하지 않아도 되기 때문에 원하는 전략을 더 유연하게 변경할 수 있다.
이렇게 인자를 통해 전략을 넘겨받는 디자인 패턴을 템플릿 콜백 패턴 이라고 스프링 내에서 부른다.