[Design Pattern] 전략 패턴
0. 들어가기 전에
이 글은 예제 코드를 이용하여 템플릿 메서드 패턴을 적용시켜보고, 어떠한 장단점이 있는지 설명하는 글입니다. 이전 글인 템플릿 메서드 패턴을 읽고 오시면 더 좋습니다.
1. 전략 패턴 적용 전
현재 주문 로직과 결제 로직이 있다.(간단하게 로그로 표현했다.)
@Slf4j
public class DefaultOrderLogic {
private void order() {
log.info("주문 로직 실행");
}
}
@Slf4j
public class DefaultPayLogic {
private void pay() {
log.info("결제 로직 실행");
}
}
그런데 여기에 요구사항으로 메서드의 시작과 끝에 로그를 출력하는 기능을 추가한다고 하자.
그렇다면 위의 코드는 아래처럼 바뀌게 된다.
@Slf4j
public class DefaultOrderLogic {
private void order() {
log.info("메서드 시작");
log.info("주문 로직 실행");
log.info("메서드 끝");
}
}
@Slf4j
public class DefaultPayLogic {
private void pay() {
log.info("메서드 시작");
log.info("결제 로직 실행");
log.info("메서드 끝");
}
}
여기까지는 할 만하다. 그렇다면 여기에 주문 취소 로직이 추가되면 어떨까?
@Slf4j
public class DefaultCancelLogic {
private void cancel() {
log.info("메서드 시작");
log.info("주문 취소 로직 실행");
log.info("메서드 끝");
}
}
그리고 모든 메서드의 실행 시간을 재는 로직이 추가되어야 한다면??
함수가 많으면 많을수록 코드를 유지보수하는 비용이 커지게된다.
(이전 글과 똑같은 내용이다)
이번에는 전력 패턴을 사용해서 위 코드를 수정할 수 있다.
2. 전략 패턴이란?
알고리즘 제품군을 정의하고, 각각 캡슐화하여 상호 교환 가능하게 하는 디자인 패턴 중 하나.
- 변하지 않는 부분을 Context, 변하는 부분을 Strategy로 인터페이스로 추상화하여 구현한다.
3. 전략 패턴 적용 후 코드
먼저 공통된 부분을 Strategy로 추상화한다.
@FunctionalInterface
public interface Strategy {
void call();
}
그 후 전략을 구현한 구현체들을 만들어 각각의 로직에 맞게 구현한다.
@Slf4j
public class OrderStrategyLogic implements Strategy {
@Override
public void call() {
log.info("주문 로직 실행");
}
}
@Slf4j
public class PayStrategyLogic implements Strategy {
@Override
public void call() {
log.info("결제 로직 실행");
}
}
@Slf4j
public class CancelStrategyLogic implements Strategy {
@Override
public void call() {
log.info("주문 취소 로직 실행");
}
}
이제 공통 로직을 가진 Context를 구현해서 전략을 구현할 수 있는데, Context를 구현하는 방법은 두 개가 있다.
3.1 Context안에 전략을 인스턴스 변수로 가지고 있는 경우
@Slf4j
public class ContextWithStrategy {
private final Strategy strategy;
public ContextWithStrategy(final Strategy strategy) {
this.strategy = strategy;
}
public void execute() {
log.info("메서드 시작");
strategy.call();
log.info("메서드 끝");
}
}
- 전략을 받아서 객체를 만들면 객체에 종속되기 때문에, 전략을 바꾸기 위해서는 새로운 객체를 생성해야 함.
3.2 Context에서 전략을 파라미터로 받아 사용하는 경우 -> 템플릿 콜백 패턴
@Slf4j
public class ContextWithoutStrategy {
public void execute(final Strategy strategy) {
log.info("메서드 시작");
strategy.call();
log.info("메서드 끝");
}
}
- 전략을 인스턴스 변수로 가지고 있지 않고, 호출하는 쪽에서 익명클래스나 람다로 전략을 주면 템플릿 콜백 패턴이 된다.
- 객체들의 관계가 런타임시에 적용되므로 좀 더 유연한 설계가 될 수 있다.
- 전략을 파라미터로 계속 넘겨줘야 사용이 가능하다.
- GOF 패턴은 아니지만, 스프링 내부에서 자주 사용하기 때문에 스프링 안에서만 이렇게 부른다.
4. 장, 단점
- 장점
- 상속을 사용하지 않아 상속의 문제점이 발생하지 않는다.
- 구현체를 바꾸기 쉬워서 유연한 설계가 될 수 있다.
- 단점
- 로직이 많아지면 구현체가 증가한다.
- 한 번 Context가 만들어지면 변경하기가 어렵다.
※ 출처
'DESIGN_PATTERN' 카테고리의 다른 글
[Design Pattern] 템플릿 메서드 패턴 (0) | 2023.02.21 |
---|