Strategy Pattern
정의
•
알고리즘 제품군을 정의하고 각각을 캡슐화하여 상호 교환 가능하게 만들자. 전략을 사용하면 알고리즘을 사용하는 클라이언트와 독립적으로 알고리즘을 변경할 수 있다.
◦
변하지 않는 알고리즘을 Context Inteface로 만든다.
◦
변하는 알고리즘을 Strategy Interface로 만든다
•
Context는 Strategy Interface에 의존
◦
Strategy의 구현체를 변경하거나 새로 만들어도 Context 코드에는 영향을 주지 않는다.
예제
•
Context
@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);
}
}
Java
복사
•
Strategy (Interface)
public interface Strategy {
void call();
}
Java
복사
•
StrategyLogic (구현체)
@Slf4j
public class StrategyLogic1 implements Strategy {
@Override
public void call() {
log.info("비즈니스 로직1 실행");
}
}
Java
복사
•
사용
◦
원하는 Strategy 구현체를 사용
/**
* 전략 패턴 사용
*/
@Test
void strategyV1() {
StrategyLogic1 strategyLogic1 = new StrategyLogic1();
ContextV1 context1 = new ContextV1(strategyLogic1);
context1.execute();
StrategyLogic2 strategyLogic2 = new StrategyLogic2();
ContextV1 context2 = new ContextV1(strategyLogic2);
context2.execute();
}
Java
복사
→ Context는 단순히 Strategy Interface에만 의존! 구현체가 변경되어도 영향이 없음
선 조립, 후 실행
•
Context의 내부 필드에 Strategy를 두고 사용하는 부분이 핵심!
•
Context와 Strategy를 실행전에 원하는 모양으로 조립해두고, 그 다음에 Context를 실행하는 선 조립, 후 실행 방식에서 매우 유용하다!
◦
스프링으로 애플리케이션 개발할 경우와 비슷 (의존관계 주입)
단점
•
Context와 Strategy를 조립한 이후에는 전략을 변경하기가 번거롭다.
실행시마다 원하는 전략을 유연하게 변경
•
Context
/**
* 전략을 파라미터로 전달받는 방식
*/
@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);
}
}
Java
복사
•
실행
@Slf4j
public class ContextV2Test {
/**
* 전략 패턴 적용 --> 파라미터로 전달
* * 그때그때 전략을 인수로 전달한당 (유연한 변경)
*/
@Test
void strategyV1() {
ContextV2 context = new ContextV2();
context.execute(new StrategyLogic1()); //구현체1
context.execute(new StrategyLogic2()); //구현체2
}
/**
* 전략패턴 익명 내부 클래스
*/
@Test
void strategyV2() {
ContextV2 context = new ContextV2();
context.execute(new Strategy() {
@Override
public void call() {
log.info("비즈니스 로직1 실행");
}
});
context.execute(new Strategy() {
@Override
public void call() {
log.info("비즈니스 로직2 실행");
}
});
}
/**
* 전략패턴 익명 내부 클래스 --> 람다
*/
@Test
void strategyV3() {
ContextV2 context = new ContextV2();
context.execute(() -> log.info("비즈니스 로직1 실행"));
context.execute(() -> log.info("비즈니스 로직2 실행"));
}
}
Java
복사
Spring에서의 Strategy Pattern - Template Callback
•
Context → Template
•
Strategy → Callback
Spring에서 JdbcTemplate, RestTemplate 등 xxxTemplate의 이름을 가진 코드
예제
•
Template
public class TraceTemplate {
private final LogTrace trace;
public TraceTemplate(LogTrace trace) {
this.trace = trace;
}
public <T> T execute(String message, TraceCallback<T> callback) {
TraceStatus status = null;
try {
status = trace.begin(message);
// 비즈니스 로직 호출
T result = callback.call();
trace.end(status);
return result;
} catch (Exception e) {
trace.exception(status, e);
throw e;
}
}
}
Java
복사
•
Callback
public interface TraceCallback<T> {
T call();
}
Java
복사