전략 패턴

동일 계열의 알고리즘군을 정의, 캡슐화하고 이를 사용하는 클라이언트와 상관없이 독립적으로 알고리즘을 서로 교환해서 사용할 수 있다.

사용하는 이유

만약 텍스트 스트림을 라인으로 구분하는 기능(다수 알고리즘이 있음)이 필요하다고 했을 때, 이 알고리즘들을 클래스에 하드코딩하는 것은 하기 이유로 바람직하지 않다.

  • 기능에 대한 구현이 아닌 줄 분리 코드가 반복적으로 생김으로써 소스가 복잡해짐
  • 때에 따라서 필요한 알고리즘이 다르기 때문에 모든 알고리즘을 제공할 필요가 없음
  • 서비스로직과 줄 분리 알고리즘이 합쳐져 있으면 알고리즘 로직을 추상화시켜서 확장 시키기 어려움
  • 위 문제 해결을 위해서 서비스로직과 각각의 줄 분리 알고리즘을 하나씩 캡슐화 한 클래스를 따로 정의하고 이 캡슐화된 알고리즘을 전략이라고 지칭한다.

구조

전략패턴 구조

비슷한 계열의 알고리즘을 가지는 Strategy 인터페이스를 구현하는 N개의 전략 객체가 있고 Context(서비스로직)에서는 concrete한 전략 객체(알고리즘) 인스턴스를 주입받아서 직접 알고리즘 로직을 호출한다.

장단점

  • 장점
    • 코드 중복 방지
    • 런타임 시에 타겟 메서드 변경 가능
    • 동일 계열 알고리즘군을 정의했기 때문에 확장성, 재사용성이 높음
  • 단점
    • 여러 전략을 사용하는 케이스가 다 다르므로 이를 이해하고 있어야함
    • 전략의 복잡도와는 상관없이 Strategy객체는 Context객체에서 전달하는 미사용 매개변수를 받아야함

예제 코드

// Strategy 인터페이스
interface BillingStrategy{
    double GetActPrice(double rawPrice);
}

// 전략1 : Normal billing strategy (unchanged price)
class NormalStrategy implements BillingStrategy{
    public double GetActPrice(double rawPrice){
        return rawPrice;
    }
}

// 전략2 : Strategy for Happy hour (50% discount)
class HappyHourStrategy implements BillingStrategy{
    public double GetActPrice(double rawPrice){
        return rawPrice * 0.5;
    }
}

// Context
class Customer{
    private List<Double> drinks;
    public BillingStrategy strategy;

    public Customer(BillingStrategy strategy){
        this.drinks = new ArrayList<>();
        this.strategy = strategy;
    }

    // 전략 패턴 사용
    public void Add(double price, int quantity){
        drinks.add(strategy.GetActPrice(price * quantity));
    }

    // Payment of bill
    public void PrintBill(){
        double sum = 0;
        for(Double i : drinks){
            sum += i;
        }
        System.out.println(("Total due: " + sum));
        drinks.clear();
    }
}interface BillingStrategy{
    double GetActPrice(double rawPrice);
}

// 전략1 : Normal billing strategy (unchanged price)
class NormalStrategy implements BillingStrategy{
    public double GetActPrice(double rawPrice){
        return rawPrice;
    }
}

// 전략2 : Strategy for Happy hour (50% discount)
class HappyHourStrategy implements BillingStrategy{
    public double GetActPrice(double rawPrice){
        return rawPrice * 0.5;
    }
}

public class StrategyMain {

    public static void main(String[] args){
        Customer firstCustomer = new Customer(new NormalStrategy());

        // Normal billing
        firstCustomer.Add(1.0, 1);

        // Start Happy Hour
        firstCustomer.strategy = new HappyHourStrategy();
        firstCustomer.Add(1.0, 2);

        // New Customer
        Customer secondCustomer = new Customer(new HappyHourStrategy());
        secondCustomer.Add(0.8, 1);
        // The Customer pays
        firstCustomer.PrintBill();

        // End Happy Hour
        secondCustomer.strategy = new NormalStrategy();
        secondCustomer.Add(1.3, 2);
        secondCustomer.Add(2.5, 1);
        secondCustomer.PrintBill();
    }
}

/*
firstCustomer  -> Total due : 2.0
secondCustomer -> Total due : 5.5
*/

참고 자료

+ Recent posts