알고리즘 제품군을 정의하고 각각을 캡슐화하여 상호 교환 가능하게 만들자. 전략을 사용하면 알고리즘을 사용하는 클라이언트와 독립적으로 알고리즘을 변경할 수 있다.
이전 포스트에서 템플릿 메서드 패턴에 대해 작성했는데, 상속을 이용하는 것이 단점이었다.
전략 패턴은 상속이 아니라 위임으로 문제를 해결하는 패턴이다.
템플릿 메서드 패턴에서는 Template 클래스를 만들고 변하는 부분(핵심 기능)을 call()로 상속받아서 처리했지만, 전략 패턴에서는 Strategy 인터페이스를 만들고 call()을 캡슐화하여 구현하도록(위임)해서 처리한다. -> 구현체의 영향을 받지 않는다.
스프링에서 사용하는 의존성 주입에서 사용하는 방식이 전략 패턴이다.
// 변하는 부분 인터페이스
public interface Strategy {
void call();
}
// 변하는 부분 구현체
@Slf4j
public class StrategyLogic1 implements Strategy{
@Override
public void call() {
log.info("비지니스 로직1 실행");
}
}
// 변하지 않는 부분(Context)
@Slf4j
@RequiredArgsConstructor
public class Context {
private final Strategy strategy;
public void execute(){
long startTime = System.currentTimeMillis();
//비지니스 로직 실행
strategy.call();
//비지니스 로직 종료
long endTime = System.currentTimeMillis();
long resultTime = endTime - startTime;
log.info("resultTime={}", resultTime);
}
}
// 테스트
@Slf4j
public class ContextTest {
@Test
void strategyV1(){
Strategy strategyLogic1 = new StrategyLogic1();
Context context1 = new ContextV1(strategyLogic1);
Context context2 = new ContextV1(new Strategy() {
@Override
public void call() {
log.info("비지니스 로직2 실행");
}
});
Context context3 = new ContextV1(() -> log.info("비지니스 로직3 실행"));
context1.execute();
context2.execute();
context3.execute();
}
}
// 결과
15:23:31.871 [main] INFO StrategyLogic1 - 비지니스 로직1 실행
15:23:31.875 [main] INFO Context - resultTime=7
15:23:31.876 [main] INFO ContextTest - 비지니스 로직2 실행
15:23:31.876 [main] INFO Context - resultTime=0
15:23:31.876 [main] INFO ContextTest - 비지니스 로직3 실행
15:23:31.876 [main] INFO Context - resultTime=0
이 방식은 선 조립, 후 실행이라서 한 번 조립하고나면 실행만 하면 되는 장점이 있다.
예를들면 스프링의 애플리케이션 로딩 시점에 의존성을 다 맺어주고 이 다음에 실제 요청을 처리하는 것이 있다. 그러나 단점으로는 유연하게 전략을 변경하기가 번거롭다.