전략패턴이란 특정한 기능을 수행하는데에 있어 다양한 알고리즘이 적용될 수 있는 경우, 이 다양한 알고리즘을 캡슐화하여 별도로 분리하는 설계 방법이다. 전략 패턴은 알고리즘들의 패밀리를 정의하고 각 패밀리를 별도의 클래스에 넣은 후 그들의 객체들을 상호교환할 수 있도록 하는 행동 디자인 패턴이다.
Strategy : 인터페이스나 추상 클래스로 외부에서 동일한 방식으로 알고리즘을 호출하는 방법을 명시
ConcreateStrategy1, 2, 3: 전략 패턴에서 명시한 알고리즘을 실제로 구현한 클래스들.
Context: 전략 패턴을 이용하는 역할 수행. 필요에 따라 동적으로 구체적인 전략을 바꿀 수 있도록 setter 메서드 제공.
한 생선 가게에서 상황에 따라 다른 가격 할인 정책을 적용한다.
1) 제일 먼저 온 손님에게 10%를 할인해주고
2) 마지막 손님은 20%
3) 그리고 신선도가 떨어진 과일에 대해서는 30% 할인을 해준다.
이를 구현하는 코드를 전략패턴 없이 작성한다면 할인 조건이 충족하는지를 if-else 분기를 타며 해결해야 한다. 이러한 코드의 문제는 하나의 메소드에 너무 많은 확인 로직이 추가되고 변경에 유연하지 않다는 점이다. 새로운 가격정책이 추가될 때마다 기존의 코드를 복사 붙여넣기 방식으로 추가해야 한다.
현재는 굉장히 단순한 코드이나 실제 프로그램단에서는 매우 복잡한 로직이 같이 존재할 것이고 복사 붙여넣기 자체가 어려울 뿐 아니라 실수로 동일 변수명을 사용한다면 더욱 위험하다. 시간이 지날수록 코드 분석이 어려워진다.
그렇다면 전략패턴을 적용한다면? 전략패턴이란 특정 컨텍스트에 다양한 알고리즘을 별도로 분리해 관리하는 것이므로 위의 상황에서는 계산이라는 컨텍스트에서 적용되는 다양한 할인 알고리즘을 별도로 관리하게 된다.
예를 들어 할인이라는 알고리즘을 DiscountPolicy 라는 인터페이스를 통해 분리하여 관리할 수 있다.
public interface DiscountPolicy {
double calculateWithDisCountRate(Item item);
}
public class FirstCustomerDiscount implements DiscountPolicy{
@Override
public double calculateWithDisCountRate(Item item) {
return item.getPrice() * 0.9;
}
}
public class LastCustomerDiscount implements DiscountPolicy{
@Override
public double calculateWithDisCountRate(Item item) {
return item.getPrice() * 0.8;
}
}
public class UnFreshFruitDiscount implements DiscountPolicy{
@Override
public double calculateWithDisCountRate(Item item) {
return item.getPrice() * 0.8;
}
}
그리고 이를 기존의 Calculator 클래스에서 생성자를 통해 필요한 하위 타입을 주입받아 사용한다.
public class Calculator {
private final DiscountPolicy discountPolicy;
public Calculator(DiscountPolicy discountPolicy) {
this.discountPolicy = discountPolicy;
}
public double calculate(List<Item> items) {
double sum = 0;
for (Item item : items) {
sum += discountPolicy.calculateWithDisCountRate(item);
}
return sum;
}
}
이렇게 되는 경우 외부에서 특정 조건의 손님에 대한 할인정책을 생성자를 통해 전달해줄 수 있다.
전략패턴을 적용할때의 장점은 컨텍스트 코드의 변경 없이 새로운 전략을 추가할 수 있다는 점이다. 즉, 요구사항이 변경되었을 때 기존의 코드를 변경하지 않아도 된다는 것이 전략패턴의 장점이며 새로운 전략에 대해서는 새로운 클래스를 통해 관리하기 때문에 OCP(Open-Close Policy)의 원칙을 준수할 수 있는 패턴이다.
모든 상황에서 전략패턴이 사용되는 것은 유용하지 않다. 컨텍스트에 적용되는 알고리즘이 하나이거나 두개인 경우는 분기를 타는 것이 편한 경우도 있다.
그러나 변경될 여지가 있고 변화의 형태가 다양함이 어느정도 보장될 때 전략패턴을 고려하는것이 추천된다.