Design Pattern
전략 패턴 (Strategy Pattern)
테리는당근을좋아해
2022. 9. 6. 02:01
전략(Strategy) 패턴
예제
public class Calculator {
public int calculator(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;
}
}
- 위 코드는 첫번째 손님인지 또는 상품의 신선도 여부에 따라 다른 가격 정책을 사용하고 있다.
문제점
- 서로 다른 계산 정책들이 한 코드에 섞여, 정책이 추가될수록 코드 분석을 어렵게 만든다.
- 정책이 추가될 때마다 메서드를 수정하는 것이 어려워지고 if-else 블록이 추가된다.
1) Strategy Pattern 적용하기
전략 패턴 적용
- 인터페이스(Strategy)로 계산 정책을 추상화
- 콘크리트 클래스(Strategy Concrete)에서 상황에 맞는 계산 정책을 제공
- 기능 자체에 대한 책임을 갖고 있는 클래스를 Context라 함
- 컨텍스트는 사용할 전략을 직접 선택하는 대신 클라이언트가 컨테스트에 사용할 전략을 전달(DI)
Context Class
// Context
public class calculator {
private DiscountStrategy discountStrategy;
// 생성자를 통한 의존 주입
public Calculator(DiscocuntStrategy discountStrategy) {
this.discountStrategy = discountStrategy;
}
public int calculator(List<Item> items) {
int sum = 0;
for (Item item : items) {
sum += discountStrategy.getDiscountPrice(item);
}
return sum;
}
}
- Context 클래스는 Strategy Interface 타입의 멤버 변수를 갖는다.
- Context 클래스는 생성자를 통해서 사용할 전략 객체를 주입받는다.
- Context 클래스는 주입 받은 전략 객체를 메서드를 통해 사용한다.
Strategy Interface
// Strategy Interface
public interface DiscountStrategy {
int getDiscountPrice(Item itme);
}
- Strategy Interface 에서는 Context에서 사용할 메서드의 정의를 담당한다.
- 구현은 Strategy Interface를 상속받는 하위 클래스에서 한다.
Strategy Concrete Class
// Strategy Concrete Class
public class FisrtGuestDiscountStrategy implements DiscountStrategy {
@Override
public int getDiscountPrice(Item itme) {
return (int) (item.getPrice() * 0.9);
}
}
- Strategy Interface를 상속받는 하위 클래스에서 메서드 정책을 구현한다.
Client Code
// client code
public class testStrategy {
private DiscountStrategy strategy;
public void onFirstGuestButtonClick() {
strategy = new FisrtGuestDiscountStrategy();
}
public void onCalculationButtonClick() {
Calculator cal = new Calculator(strategy);
int price = cal.calcaulate(items);
}
}
- 전략 객체는 Context를 사용하는 클라이언트에서 직접 생성
- 컨텍스트를 사용하는 클라이언트가 전략의 상세 구현에 대한 의존이 발생
- 컨텍스트의 클라이언트가 전략의 인터페이스가 아닌 상세 구현을 안다는 것이 문제처럼 보일 수 있으나, 전략의 콘크리트 클래스와 클라이언트의 코드가 쌍을 이루기 때문에 유지 보수 문제가 발생할 가능성이 줄어듦
- 클라이언트 코드에서 전략 객체를 직접 생성하는 것은 오히려 코드 이해를 높이고 코드 응집을 높여주는 효과
2) 전략 패턴의 이점
- 컨텍스트 코드의 변경 없이 새로운 전략을 추가( 클라이언트 코드에 객체를 생성해 주기만 하면 된다.)
- 전략 패턴을 적용함으로써 확장에는 열려 있고 변경에는 닫혀 있음 → 개방 폐쇄 원칙을 따르는 구조
3) 전략 패턴을 적용할 수 있는 경우
- if-else로 구성된 코드 블록이 비슷한 알고리즘을 수행하는 경우 전략 패턴을 적용함으로써 코드를 확장 가능하도록 변경할 수 있음
- 완전히 동일한 기능을 제공하지만 성능의 장단점에 따라 알고리즘을 선택해야하는 경우에 사용할 수 있음