728x90
728x90
반응형
Strategy- 전략 패턴
- 로직을 정의하고 이를 캡슐화하여 서로 교환 가능하게 만드는 패턴
- 상속이 아닌 인터페이스를 위임받아 구현하는 방식으로 상속으로 인해 생기는 문제를 해결
구성 요소
- Context: 전략 객체를 사용하는 클래스로 템플릿과 같은 변하지 않는 코드를 다루는 클래스
- Strategy Interface: 다양한 로직을 정의하는 인터페이스로 전략 클래스는 해당 인터페이스를 상속받아 구현
- Concrete Strategies: 인터페이스를 구현하여 특정 로직을 제공하는 클래스
사용 방법
- 변하는 로직을 정의하는 인터페이스를 정의
- 인터페이스를 통해 특정 로직을 구현한 클래스 생성
- 공통된 로직을 정의하는 Context 클래스를 생성
//strategy interface - 변경되는 로직을 정의하는 인터페이스
public interface Strategy {
void call();
}
//interface를 상속받아 구현한 클래스
public class StrategyLogic1 implements Strategy{
@Override
public void call() {
log.info("로직1 실행");
}
}
필드에 전략을 보관하는 방식
- Context 클래스 내에 전략을 필드로 두고 생성 시 주입받아 사용하는 방식
- 스프링에서 의존관계를 주입받아 사용하는 것과 동일한 방식 - 선 조립 후 실행
- 전략 변경 시 새로운 전략으로 객체를 생성하거나 변경이 어려움
//공통된 로직이 정의된 Context 클래스 - 전략을 필드에 보관하고 주입 받는 방식
public class Context {
private final Strategy strategy;
public Context(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); //공통 로직
}
}
//사용
StrategyLogic1 strategyLogic1 = new StrategyLogic1();
Context context = new Context(strategyLogic1);
context.execute();
//익명 클래스
Strategy strategy = new Strategy() {
@Override
public void call() {
log.info("로직1 실행");
}
};
Context context = new Context(strategy);
context.execute();
//람다 - interface에 정의된 메서드가 1개인 경우
Context context = new Context(() -> log.info("로직1 실행"));
context.execute();
파라미터로 전략을 넘기는 방식 - Template Callback
- 변경할 전략을 실행 시 파라미터로 넘기는 방식
- 실행하는 메서드에서 직접 파라미터로 넘겨받아 변경 유연
- 스프링에서 템플릿-콜백 패턴으로 불림
- 변하지 않는 부분 template(Context)과 변하는 부분 strategy로 코드가 호출은 되는데 코드를 넘겨준 곳 뒤에서 실행되어 콜백이라 불림
- 클라이언트는 strategy의 함수를 직접 호출하지 않고 template에서 함수를 실행할 때 strategy를 넘겨주고 뒤(template)에서 실행
- 스프링의 ...template 는 모두 이러한 방식으로 구현되어 있음
- 매번 전략을 파라미터로 넘겨야 하는 번거로움이 있음
//실행 시 파라미터로 넘겨 받는 방식
public class Context {
public void execute(Strategy strategy) {
long startTime = System.currentTimeMillis(); //공통 로직
strategy.call(); //변경되는 로직
long endTime = System.currentTimeMillis(); //공통 로직
long resultTime = endTime - startTime; //공통 로직
log.info("resultTime={}", resultTime); //공통 로직
}
}
//사용
Context context = new Context();
context.execute(new StrategyLogic1());
//익명 클래스
Strategy strategy = new Strategy() {
@Override
public void call() {
log.info("로직1 실행");
}
};
Context context = new Context();
context.execute(strategy);
//람다 - interface에 정의된 메서드가 1개인 경우
Context context = new Context();
context.execute(() -> log.info("로직1 실행"));
장점
- 새로운 로직 추가나 기존 로직 변경이 쉬움
- 로직이 컨텍스트와 분리되어 가독성과 유지보수가 편함
- 템플릿 메서드와 다르게 상속이 아닌 위임을 통해 구현되므로 단순한 인터페이스에만 의존
단점
- 전략을 선택하는 로직이 컨텍스트에 포함되어 복잡성이 증가할 수 있음
- 전략 객체가 상태를 가지게 되면 상태 관리가 복잡해짐
- 상속은 아니지만 인터페이스 위임받아 특정 인터페이스에 의존하게 되므로 새로운 전략 도입 시 수정이 생길 수 있음
스프링 핵심 원리 - 고급편 강의 | 김영한 - 인프런 강의 내용 참고
728x90
'백엔드 > Spring' 카테고리의 다른 글
[Spring] 동적 프록시 (0) | 2024.11.05 |
---|---|
[Spring] Proxy & Decorator Pattern (0) | 2024.11.02 |
[Spring] Template Method (0) | 2024.10.31 |
[Spring] Thread Pool & ThreadLocal (0) | 2024.10.30 |
[Spring] 모니터링 환경 구성 (0) | 2024.10.12 |