ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 전략 패턴 (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://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EC%A0%84%EB%9E%B5Strategy-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90

     

    💠 전략(Strategy) 패턴 - 완벽 마스터하기

    Strategy Pattern 전략 패턴은 실행(런타임) 중에 알고리즘 전략을 선택하여 객체 동작을 실시간으로 바뀌도록 할 수 있게 하는 행위 디자인 패턴 이다. 여기서 '전략'이란 일종의 알고리즘이 될 수

    inpa.tistory.com

    https://product.kyobobook.co.kr/detail/S000001062523

     

    객체 지향과 디자인 패턴 | 최범균 - 교보문고

    객체 지향과 디자인 패턴 | 개발자가 반드시 정복해야 할 『객체 지향과 디자인 패턴』. 자바나 C#과 같은 객체 지향 언어는 익혔지만 객체 지향 자체에 대한 이해가 부족한 개발자를 위해 가능

    product.kyobobook.co.kr

     

    '언어 > 디자인패턴' 카테고리의 다른 글

    템플릿 메서드(Template Method) 패턴  (0) 2024.05.26
Designed by Tistory.