OOP

예시로 이해하는 전략(Strategy) 패턴

짜비 2021. 6. 27. 16:30

첫 번째 손님, 그리고 신선하지 않은 과일에 대해 할인을 적용하는 과일 가게를 생각해보자.

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

간단한 코드지만 만약 할인 정책이 계속해서 추가된다면 if-else 문이 늘어나고 calculate의 parameter도 증가해 코드가 복잡해질 것이다.

 

이를 해결하기 위한 방법으로, 가격할인정책을 별도의 객체로 분리할 수 있다.

위와 같이 DiscountStrategy 인터페이스를 따로 만들었다. 그리고 개별적인 할인정책들은 이 DiscountStrategy를 implement하도록 했다.

이때 가격 계산 기능 책임을 가지고 있는 Caculator를 Context라고 부르고, 가격 할인 알고리즘(계산 방법)을 추상화하고 있는 interface DiscountStrategy를 Strategy라고 부른다. 이처럼 어떤 Context로부터 알고리즘(전략)을 분리하는 설계 방법이 Strategy 패턴이다.

 

Context는 자신이 사용할 전략을 DI(Dependency Injection)을 통해 전달받는다. 

 

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);
        }
        return sum;
    }
}

수정된 Calculator 클래스를 보면, 생성자를 통해 가격할인 전략을 주입받는 걸 볼 수 있다. 그리고 item의 가격을 계산할 때 가격할인 전략 객체를 사용한다.

 

그렇다면 Context(Calculator)의 client가 Context를 사용하는 코드를 한번 살펴보자.

private DiscountStrategy strategy;

public void onFirstGuestButtonClick(){
//첫 손님 할인 버튼 누를 때 객체 생성
	strategy = new FirstGuestDiscountStrategy();
}

public void onCalculationButtonClick(){
//계산 버튼 누를 때 실행
	Caculator cal = new Calculator(strategy);
    int price = cal.calculate(items);
    ...
}

이처럼 Context를 사용하는 코드에서 구체적인 가격할인전략 객체를 생성한다. 이때 클라이언트 코드가 가격할인전략의 상세 구현을 안다는 것이 문제처럼 보일 수 있지만, 이 경우에는 상세 구현 클래스와 클라이언트의 코드가 밀접히 연관되어 있기 때문에 유지 보수 문제로부터 자유롭다.

예를 들어, 새로운 할인 정책이 추가될 경우, 클라이언트의 코드(버튼)가 새로 추가되고, 이 코드에 새로운 할인 정책의 구현 클래스도 함께 추가해주면 된다. 삭제할 때에도 클라이언트의 코드, 할인 정책의 구현 클래스 코드를 함께 삭제해주면 된다.

 

전략 패턴을 사용할 때의 장점은, Context(Calculator) 코드의 변경 없이 새로운 코드를 추가할 수 있다는 점이다. 예를 들어 마지막 손님에게 할인을 해준다는 정책을 추가한다면 Calculator 코드는 변경할 필요 없이, LastGuestDiscountStrategy 클래스를 생성하고, 클라이언트에서 버튼을 새로 하나 추가하면 된다.

다시 말해, Calculator 클래스가 개방폐쇄 원칙을 따르게 된 것이다.

 

일반적으로 if-else로 구성된 코드가 비슷한 기능을 수행하는 경우, 동일한 기능을 제공하지만 성능의 장단점에 따라 알고리즘을 선택해야 하는 경우, 전략 패턴을 적용하면 확장이 용이한 코드로 변경할 수 있다.

 

 

코드 출처, 참고 도서 : 개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴, 최범균 지음, 인투북스