참고
전략 패턴
NOTE
하나의 메시지와 책임을 정의하고, 이를 수행할 수 있는 다양한 전략을 만든 후, 다형성을 통해 전략을 선택해 구현을 실행하는 패턴.
•
필요 개념: 컴포지션
•
활용도: 부담없이 사용해볼만 함
•
난이도: 간단
•
패턴이 필요한 상황: 상속 상황에서 내부 코드의 빈번한 중복
[ 참고]
•
템플릿 메서드와 차이점
◦
템플릿 패턴
▪
런타임에 타입선택
▪
추상 메서드로 의존성 역전
▪
최악의 경우 조합폭발(class가 무수히 많아짐)
◦
전략 패턴
▪
런타임에 합성(조립)
▪
추가 인터페이스로 의존성 분산
▪
의존성 폭발 발생이 생길 수 있음
전략 패턴 V1
NOTE
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 인터페이스
public interface Strategy {
void call();
}
Java
복사
@Slf4j
public class StrategyLogic1 implements Strategy {
@Override
public void call() {
log.info("비즈니스 로직1 실행");
}
}
Java
복사
@Slf4j
public class StrategyLogic2 implements Strategy {
@Override
public void call() {
log.info("비즈니스 로직2 실행");
}
}
Java
복사
execute 호출
// 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
복사
// 익명클래스 사용
@Test
void strategyV2(){
Strategy strategyLogic1 = new Strategy() {
@Override
public void call() {
log.info("비즈니스 로직1 실행");
}
};
ContextV1 contextV1 = new ContextV1(strategyLogic1);
log.info("strategyLogic1={}", strategyLogic1.getClass());
contextV1.execute();
Strategy strategyLogic2 = new Strategy() {
@Override
public void call() {
log.info("비즈니스 로직2 실행");
}
};
ContextV1 contextV2 = new ContextV1(strategyLogic2);
log.info("strategyLogic2={}", strategyLogic2.getClass());
contextV2.execute();
}
Java
복사
// 익명 클래스 사용 인스턴스 생성하여 파라미터 전달
@Test
void strategyV3(){
ContextV1 contextV1 = new ContextV1(new Strategy() {
@Override
public void call() {
log.info("비즈니스 로직1 실행");
}
});
contextV1.execute();
ContextV1 contextV2 = new ContextV1(new Strategy() {
@Override
public void call() {
log.info("비즈니스 로직2 실행");
}
});
contextV2.execute();
}
Java
복사
// 람다식 사용
@Test
void strategyV4(){;
ContextV1 contextV1 = new ContextV1(() -> log.info("비즈니스 로직1 실행"));
contextV1.execute();
ContextV1 contextV2 = new ContextV1(() -> log.info("비즈니스 로직2 실행"));
contextV2.execute();
}
Java
복사
하나의 메서드를 갖는 인터페이스는 람다식을 통해 익명 클래스 인스턴스를 생성할 수 있다.
전략 패턴 V2
NOTE
Strategy 인터페이스를 Context 클래스의 필드로 갖지않고, execute 호출마다 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
복사
Strategy 인터페이스
public interface Strategy {
void call();
}
Java
복사
@Slf4j
public class StrategyLogic1 implements Strategy {
@Override
public void call() {
log.info("비즈니스 로직1 실행");
}
}
Java
복사
@Slf4j
public class StrategyLogic2 implements Strategy {
@Override
public void call() {
log.info("비즈니스 로직2 실행");
}
}
Java
복사
execute 호출
// Strategy 구현 클래스 생성
@Test
void strategyV1(){
ContextV2 context = new ContextV2();
context.execute(new StrategyLogic1());
context.execute(new StrategyLogic2());
}
Java
복사
// 익명클래스 사용
@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 실행");
}
});
}
Java
복사
// 람다식 사용
@Test
void strategyV3(){
ContextV2 context = new ContextV2();
context.execute(() -> log.info("비즈니스 로직1 실행"));
context.execute(() -> log.info("비즈니스 로직2 실행"));
}
Java
복사
하나의 메서드를 갖는 인터페이스는 람다식을 통해 익명 클래스 인스턴스를 생성할 수 있다.
전략 패턴 V1 vs V2
NOTE
V1 (필드 방식)
•
Strategy 구현체를 Context에 주입받아서, execute를 호출만 하면되므로 간편하다
•
하지만 다른 전략을 사용하려면, 다른 Strategy 구현체를 주입받은 새로운 Context객체를 생성해야 한다.
V2(파라미터 방식)
•
Strategy 구현체를 execute메서드 파라미터로 받는다.
•
execute호출마다 파라미터를 넘겨야하지만, execute호출마다 유연하게 전략을 변경할 수 있다.