TIL/디자인 패턴

디자인 패턴 - 전략 패턴 (Strategy Pattern)

야아옹 2020. 11. 24. 17:40

전략 패턴 또는 정책 패턴이라고 하며 이는 실행 중에 알고리즘을 선택할 수 있게 하는 패턴

 

예를 들어 과일 매장에서 상황에 따른 가격할인 정책을 적용하고 있다고 가정한다.

 1. 매장에 들어온 첫번째 손님 할인정책

 2. 저녁시간대에 신선도가 떨어진 과일에 대한 할인정책

 

 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;
     }
 }

위 코드에선 다음의 문제를 포함하고있다.

 

 1. 서로 다른 계산 정책들이 한 코드에 섞여 있어, 정책이 추가될 수록 코드 분석을 어렵게 만든다.

 2. 가격 정책이 추가될때 마다 Calculate 매서드를 수정하는것이 점점 어려워진다. 

    Ex) 마지막 손님 50% 할인 과 같은 정책이 추가될 경우 LastGuest 파라미터 및 if 블록이 하나더 추가 되어야한다.

 

이러한 문제를 해결하기 위한 방법중 하나는 가격 할인 정책을 별도 객체로 분리 하는것이다.

 

1. DiscountStrategy 인터페이스는 상품의 할인 금액 계산을 추상화

2. 각 콘크리트 클래스(FirstGuestDiscountStrategy, NonFresh...) 는 상황에 맞는 할인 계산 알고리즘 제공

3. Calculator 클래스는 가격 합산 계산의 책임

※ 여기서 가격 할인 알고리즘을 추상화 하고있는 DiscountStrategy 를 전략(Strategy) 라고 부르며,

    Calculator는 콘텍스트(Context) 불려진다.

 

 

1. DiscountStrategy Interface 

public interface DiscountStrategy {
    int getDiscountPrice(Item item);
}

 

2. DiscountStrategy 인터페이스를 구현한 콘크리트 클래스

public class FirstGuestDiscountStrategy implements DiscountStrategy{
    @Override
    public int getDiscountPrice(Item item)
    {
        return (int) (item.getPrice() * 0.9);
    }
}
public class NonFreshDiscountStrategy implements DiscountStrategy{
    @Override
    public int getDiscountPrice(Item item)
    {
        return (int) (item.getPrice() * 0.8);
    }
}

 

3. Calculator 클래스

import java.util.List;

public class Calculator {

    private DiscountStrategy _discountStrategy;

    public Calculator(DiscountStrategy discountStrategy)
    {
        this._discountStrategy = discountStrategy;
    }

    public int calculate(List<Item> items)
    {
        int sum = 0;
        for (Item item: items)
        {
            sum += _discountStrategy.getDiscountPrice(item);
        }
        System.out.println("items = " + sum);
        return sum;
    }
    public void onFirstGuestButton()
    {
        _discountStrategy = new FirstGuestDiscountStrategy();
    }
    public void onCalculationButton(List<Item> items)
    {
        Calculator _cal = new Calculator(_discountStrategy);
        int price = _cal.calculate(items);
    }

}

 

4. Main Class 및 Item Class

import java.util.ArrayList;

public class Main {
    private static Calculator _cal;
    public static void main(String[] args) {
        _cal.onFirstGuestButton();
        ArrayList<Item> _list = new ArrayList<>();
        _list.add(new Item(1000));
        _list.add(new Item(2000));
        _cal.onCalculationButton(_list);
    }

}
public class Item {
    int price = 0;
    boolean isfresh = true;
    int getPrice() {return this.price;}
    boolean isFresh() {return  this.isfresh;}

    public Item(int somePrice)
    {
        this.price = somePrice;
    }

}

 

위코드 Calculator 를 사용하는 코드에서 FirstGuestDiscountStrategy 클래스의 객체를 생성하는것을 알 수 있다.

이는 콘텍스트를 사용하는 클라이언트가 전략의 상세구현에 대한 의존이 발생한다는 것을 뜻한다.

 

전략 패턴을 적용할때 얻는이점은 콘텍스트 코드의 변경없이 새로운 전략을 추가 할 수 있다는 점이다.

예를들어 마지막 손님에게 대폭할인 정책을 추가하는경우 계산의 틀을 제공하는 Calculator 클래스의 코드는 변경되지 않으며, 단지 새로운 할인 정책을 구현한 lastGuestDiscountStrategy 클래스를 추가하고, 마지막 손님 할인 버튼을 추가 하면 끝이 난다.

 

일반적으로 if-else 로 구성된 코드 블록이 비슷한 기능(비슷한 알고리즘) 을 수행하는 경우에 전략 패턴을 적용함으로써코드를 확장가능하도록 변경할 수 있다. 

또한 완전히 동일한 기능을 제공하지만 성능의 장단점에 따라 알고리즘을 선택해야 하는경우에도 전략 패턴을 사용한다.

 

출처 : 개발자가 반드시 정복해야할 객체 지향과 디자인 패턴

 

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

디자인 패턴이란?  (0) 2020.11.24