개요
템플릿 메서드는 상속을 이용한 패턴이다보니 상속에 관한 단점들을 그대로 안고갔다. 이러한 문제를 전략 패턴을 통해 풀어보겠다.
템플릿 메서드 패턴을 알고싶다면 여기 를 클릭하면 된다.
전략 패턴(Strategy Pattern)이란?
실행 중에 알고리즘을 선택할 수 있게 하는 디자인 패턴이다. 캡슐화한 특정 계열의 알고리즘들을 정의하고 이 알고리즘들을 해당 계열 안에서 상호 교체가 가능하게 만든다.
쉽게 설명하자면 변하지 않는 부분(Context)라는 곳에 두고, 변하는 부분을 Strategy라는 인터페이스를 만들고 해당 인터페이스를 구현하도록 해서 문제를 해결한다. 상속이 아니라 위임으로 문제를 해결하는 것이다.
예제를 통한 전략패턴 알아보기
말로 이해하기 어려우므로 예제를 통해 알아보자.
Strategy.java
public interface Strategy {
void call();
}
StrategyLogic1.java
@Slf4j
public class StrategyLogic1 implements Strategy {
@Override
public void call() {
log.info("비즈니스 로직 1 실행");
}
}
StrategyLogic2.java
@Slf4j
public class StrategyLogic2 implements Strategy {
@Override
public void call() {
log.info("비즈니스 로직 2 실행");
}
}
ContextV1.java
/**
* 필드에 전략을 보관하는 방식
*/
@Slf4j
public class ContextV1 {
private Strategy strategy;
public ContextV1(Strategy strategy) {
this.strategy = strategy;
}
public void execute(){
long startTime = System.currentTimeMillis();
// 비즈니스 로직 실행
strategy.call();
// 비즈니스 로직 종료
long endTime = System.currentTimeMillis();
long resultTime = endTime - startTime;
log.info("resultTime={}", resultTime);
}
}
ContextV1은 변하지 않는 로직을 가지고 있는 템플릿 역할을 하는 코드이다. 전략 패턴에서는 이것을 Context(문맥)이라 한다.
쉽게 말하자면 Context는 변하지 않지만 그 Context 속에서 strategy를 통해 일부 전력이 변경된다고 생각하면 된다.
Context 내부에 변하는 부분이 Strategy의 구현체를 주입하면 된다. 이 전략 패턴의 핵심은 Context는 Strategy 인터페이스에만 의존한다는 점이다. 덕분에 Strategy의 구현체를 변경하거나 새로 만들어도 Context는 코드에는 영향을 주지 않는다.
@Test
void strategyV1() {
StrategyLogic1 strategyLogic1 = new StrategyLogic1();
ContextV1 contextV1 = new ContextV1(strategyLogic1);
contextV1.execute();
StrategyLogic2 strategyLogic2 = new StrategyLogic2();
ContextV1 contextV2 = new ContextV1(strategyLogic2);
contextV2.execute();
}
Strategy의 구현체인 StrategyLogic1, StrategyLogic2를 각각 생성 후 Context에 주입하는 것을 확인할 수 있다. 이렇게 해서 Context안에 원하는 전략을 주입한다.
어디서 많이 본 코드 같다고 하면 맞다. 바로 스프링에서 의존관계 주입할 때 사용하는 방식이 바로 전략 패턴이다.
Strategy를 파라미터로 전달해보자
/**
* 전략을 파라미터로 전달받는 방식
*/
@Slf4j
public class ContextV2 {
public void execute(Strategy strategy){
long startTime = System.currentTimeMillis();
// 비즈니스 로직 실행
strategy.call(); // 위임
// 비즈니스 로직 종료
long endTime = System.currentTimeMillis();
long resultTime = endTime - startTime;
log.info("resultTime={}", resultTime);
}
}
ContextV2는 필드에 Strategy를 선언하지 않고 파라미터로 전달 받아 실행시킨다. 한번 실행해보자.
@Test
void strategyV1(){
ContextV2 context = new ContextV2();
context.execute(new StrategyLogic1());
context.execute(new StrategyLogic2());
}
Context와 Strategy를 선 조립 후 실행하는 것이 아니라 Context를 실행할 때 마다 Strategy를 생성해서 파라미터로 전달하는 방식이다.
클라이언트는 Context를 실행하는 시점에 원하는 Strategy를 전달할 수 있다. 따라서 이전 방식과 비교해서 원하는 전략을 더욱 유연하게 변경할 수 있다.
정리
- ContextV1(필드 조립 방식)은 필드에 Strategy를 저장하는 방식으로 전략패턴 구사.
- 선 조립 후, 실행 방법에 적합
- Context를 실행하는 시점에 이미 조립이 끝났기 때문에 단순히 실행만 하면 된다.
- ContextV2(파라미터 전달 방식)는 파라미터로 Strategy를 전달받는 방식으로 전략 패턴 구사.
- 실행할 때 마다 전략을 유연하게 변경 가능.
- 실행할때 마다 전략을 계속 지정해주어야 한다는 점이 단점.
'Web' 카테고리의 다른 글
[Spring] 예제를 통해 프록시(Proxy), 데코레이터(Decorator) 패턴에 대해 알아보기 (0) | 2025.01.02 |
---|---|
[Spring] 예제를 통해 템블릿 메서드 패턴에 대해 알아보기 (0) | 2024.12.31 |
[Spring Boot] Test 클래스에서 lombok을 사용하고 싶을 때 (1) | 2024.11.18 |
서블릿과 멀티쓰레드 이해 및 HTTP API, CSR/SSR 개념 정리 (2) | 2024.09.21 |