-
전략 패턴 (Strategy)언어/디자인패턴 2024. 5. 26. 21:41
전략 패턴이란?
- 실행(런타임) 중 알고리즘 전략을 선택하여 객체 동작을 실시간으로 바뀌도록 하는 행위 디자인 패턴이다.
- 전략은 일종의 알고리즘 혹은 특정 목표를 수행하기 위한 행동 계획을 의미한다.
- 어떤 일을 수행하는 알고리즘이 여러가지 일때, 동작들을 미리 전략으로 정의하여, 손쉽게 교체하는 방법이다. 알고리즘 변형이 빈번할 경우 적합하다.
- 전략 인터페이스 : 모든 전략 구현체에 대한 공용 인터페이스
- 전략 객체(concreate): 전략 인터페이스 구현체
- 컨텍스트(Context) : 알고리즘을 실행할 때 해당 알고리즘과 연결된 전략 객체 메서드 호출 (전략을 사용하는 큰 기능)
- 클라이언트 : 전략 객체 전달, 해당 전략 사용해서 결과 얻음
class Client{ public static void main(String[] args){ // 1. 컨텍스트 생성 Context c = new Context(); // 2. 전략 설정 c.setStrategy(new ConcreteStrateyA()); // 3. 전략 실행 c.doSomething(); // 4. 다른 전략 설정 c.setStrategy(new ConcreteStrateyB()); // 5. 다른 전략 시행 c.doSomething(); } } class Context { IStrategy Strategy; // 전략 인터페이스를 합성(composition) // 전략 교체 메소드 void setStrategy(IStrategy Strategy) { this.Strategy = Strategy; } // 전략 실행 메소드 void doSomething() { this.Strategy.doSomething(); } } interface IStrategy { void doSomething(); } // 전략 알고리즘 A class ConcreteStrateyA implements IStrategy { public void doSomething() {} } // 전략 알고리즘 B class ConcreteStrateyB implements IStrategy { public void doSomething() {} }
- 전략 인터페이스와 전략 객체
- 전략을 등록할 클라이언트
- 전략을 사용할 context
* 전략의 등록,실행,수행!
사용시기 및 주의점
사용시기
- if-else로 구성된 코드 블록이 비슷한 기능(알고리즘) 수행하는 경우 이를 적용해서 확장 가능하게 바꿀 수 있다.
//비슷한 기능을 수행하는 if-else문 public class Calculator{ public int calculate(boolean firstGuest, List<Item> items){ int sum=0; for(Item item: items){ if(firstGuest){ //.. }else if(!item.isFresh()){ //.. } //.. } } }
- 완전 동일한 기능을 제공하지만, 성능의 장단점에 따라 알고리즘을 선택하는 경우도 적용 가능하다.
주의점
- 알고르즘이 많아질수록 관리해야할 객체의 수가 늘어남
- 만일 어플리케이션 특성이 알고리즘이 많지 않고 자주 변경되지 않는다면, 굳이 사용할 이유 없음
- 개발자는 적절한 전략을 선택하기 위해 전략 간의 차이점을 파악하고 있어야함(복잡하다!)
예시
할인 정책
public class Calculator { public int calculate(boolean firstGuest, List<Item> items){ int sum = 0; for(Item item: items){ if(firstGuest){ sum+= (int)(item.getPrice()*0.9); }else if(!item.isFresh()){ sum+= (int)(item.getPrice()*0.8); }else{ sum+=item.getPrice(); } } return sum; } }
- 첫 손님 혹은 신선하지 않은 물건에 대해 할인 정책이 각각 다른 메서드가 있다.
- 위 메서드의 문제점은 다음과 같다.
- 정책이 추가될 수록 코드 분석이 어려워진다.
- 정책이 추가될 수록 메서드 수정이 어려워진다. 만약 새로운 가격 정책을 수행하는데 새로운 상태(ex 재방문자인가?)를 추가해야한다면, 메서드를 다시 정의해야한다. (OCP 위배)
- 이를 전략 패턴으로 개선해보자
- Calculator가 컨텍스트 (전략 사용, 결과 반환)
- Client가 전략 등록
- DiscountStrategy인터페이스 생성
public interface DiscountStrategy { int getDisCountPrice(Item item); } //혹은 아래와 같이 더 자잘하게 쪼갤 수도 있다. (필요에 따라) public interface ItemDiscountStategy { int getDisCountPrice(Item item); } public interface TotalPriceDiscountStrategy { int getDisCountPrice(Item item); }
//클라이언트는 버튼을 눌러서 첫 고객인지, 뭔지 전략을 등록할 수 있음 public class Client { private DiscountStrategy strategy; public void onFirstGuestButtonClick(){ strategy = new FirstGuestDiscountStrategy(); } public void onCalculationButtonClick(){ Calculator cal = new Calculator(strategy); int price = cal.calculate(new ArrayList<Item>()); } }
public class Calculator { private DiscountStrategy strategy; public Calculator(DiscountStrategy strategy) { this.strategy = strategy; } public int calculate(List<Item> items){ int sum = 0; for(Item item :items){ sum+=strategy.getDisCountPrice(item); } return sum; } }
- 기존의 if문을 제거하고, 간단하게 전략 사용
- 신선하지 않은 제품의 계산을 위해 NonFreshItemDiscountStrategy를 간단하게 추가할 수 있다.
- 위 코드에선 클라이언트의 특정 행위(메서드)와 전략의 등록을 이어주었다.
- 이를 통해 전략 콘크리트 클래스와 클라이언트 코드가 쌍을 이루기 때문에 코드 응집도를 높일 수 있다.
- 새로운 할인 사항이 (대규모할인) 등이 발생했다 사라질 때 쉽게 확장이 가능하며, 전략과 메서드가 함께 사라져 유지보수에도 이점이 있다.
참고자료
https://product.kyobobook.co.kr/detail/S000001062523
'언어 > 디자인패턴' 카테고리의 다른 글
템플릿 메서드(Template Method) 패턴 (0) 2024.05.26