[디자인 패턴] 전략 패턴(Strategy Pattern) By starseat 2023-07-30 17:00:43 알고리즘 Post Tags # 전략 패턴(Strategy Pattern) - 알고리즘군을 정의하고 캡슐화해서 각각의 알고리즘군을 수정해서 쓸 수 있게 해줌. - 전략 패턴을 사용하면 클라이언트로부터 알고리즘을 분리해서 독립적으로 변경 가능 # 들어가기 오리를 정의하는 클래스 다이어그램의 초기 모델은 다음과 같다. ![image.png](/uploads/_temp/20230730/147d6cd2caf3a475c25a9884221d07af.png) # 기능 추가 - fly() 이 기능에 `fly()` 를 추가하게 되면 어떻게 해야 될까? 가장 간단한 방법은 `Duck` 에 `fly()` 를 추가 하면 될 것이다. ![image.png](/uploads/_temp/20230730/5d32bfa6f7da47c04807c50d6f04c56b.png) ## 기능 추가 - fly() 의 문제 하지만 `Duck` 을 상속 받아 사용하고 있다면? 고무 오리(RubberDuck) 는 날지 못한다는 것이다. ![image.png](/uploads/_temp/20230730/2897c9ab2f74b4666829c87d24adb5b2.png) 하지만 이런 방식이라면 **새끼 오리 나 오리 장식품 등** 날지 못하는 오리 모두 오버라이드 해야 한다는 상황이 발생한다. (**코드 중복**) # 두번째 고려사항 오리는 울음소리가 제각각 이므로 `quack()` 도 `fly()` 와 마찬가지로 별도로 구현해야 한다. # 해결 방법 고민하기 모든 서브클래스에서 `flay()` 와 `quack()` 가 필요한 기능이 아니므로 상속은 올바른 방법이 아니다. 그렇담 .**Flyable**, **Quackable** 라는 인터페이스를 만드는 것도 한 방법이지만, 코드를 재사용하지 않으므로 코드 관리에 커다란 문제가 생길 수 있다. (오리 마다 나는 방식도 다를 수 있음.) 즉 한가지 행동을 바꿀 때마다 그 행동이 정의되어 있는 서로 다른 서브클래스를 전부 찾아서 소스를 일일이 고쳐야 하고, 그 과정에서 버그가 생길 수 있다. ## 오리의 행동 구현 오리의 행동을 분리하여 구현한다. `FlyBehavior`, `QuackBehavior` 인터페이스를 생성하여 필요한 정보를 기입한다. (*전략적 사용*) ![image.png](/uploads/_temp/20230730/b2bf9abe308c962c621ba604dd40ba34.png) 즉, 메소드를 정의해서 구현하지 않고 다른 클래스에 **위임** 대신 위임한 기능을 사용하는 method 가 필요함. ## 구현된 행동 적용 행동을 별도의 인터페이스로 분리 하였으니 **Duck** 클래스는 다음과 같이 변경된다. ![image.png](/uploads/_temp/20230730/81b74b8b35f58e5b2b1e67d980719500.png) `fly()` 와 `quack()` 대신 `performQuack()` 와 `performFly()` 가 추가되었다. **동적으로 행동을 지정** 하기 위한 방법도 생각해야 한다. 여기선 간단한 방법인 `setter` 를 사용하여 동적 행동을 지정한다. # 소스로 보기 이 예저의 최상위 클래스인 **Duck** 와 자식 클래스인 **MallardDuck** 를 구현한 샘플 java 코드이다. ## 기본 클래스 ### Duck ```java public abstract class Duck { FlyBehavior flyBehavior; QuackBehavior quackBehavior; // quack() 를 직접 처리하는 대신 // quackBehavior 로 참조되는 객체에 그 행동을 위힘 public void performQuack() { quackBehavior.quack(); } public void performFly() { flyBehavior.fly(); } public abstract void display(); public void swim() { System.out.println("첨벙 첨벙"); } // 동적 행동 지정 public void setFlyBehavior(Flybehavior fb) { this.flyBehavior = fb; } public void setQuackBehavior(QuackBehavior qb) { this.quackBehavior = qb; } } ``` ### MallardDuck ```java public class MallardDuck extends Duck { public MallardDuck() { // 일반적인 오리이므로 "꽥꽥" 이 가능한 Quack 사용 // performQuack() 이 호출되면 Quack 객체에 위임됨. quackBehavior = new Quack(); // 마찬가지로 일반적인 오리이므로 날개로 날 수 있는 FlyWithWings 사용 // performFly() 이 호출되면 FlyWithWings 객체에 위임됨. flyBehavior = new FlyWithWings(); } public void display() { System.out.println("나는 청둥오리닷!"); } } ``` ## 행동 인터페이스 ### FlyBehavior - 인터페이스 ```java public interface FlyBehavior { public void fly(); } ``` - 구현체 ```java public class FlyWithWings implements FlyBehavior { public void fly() { System.out.println("날개로 난다"); } } --- public class FlyNoWay implements FlyBehavior { public void fly() { System.out.println("날지 못한다..."); } } ``` ### QuackBehavior - 인터페이스 ```java public interface QuackBehavior { public void quack(); } ``` - 구현체 ```java public class Quack implements QuackBehavior { public void quack() { System.out.println("꽥꽥"); } } --- public class MuteQuack implements QuackBehavior { public void quack() { System.out.println("..."); } } --- public class Squack implements QuackBehavior { public void quack() { System.out.println("삑!"); } } ``` ## 사용 예제 ```java public class MallardDuckSimulator { public static void main(String[] args) { Duck mallard = new MallardDUck(); mallard.display(); mallard.performQuack(); mallard.performFly(); mallard.setQuackBehavior(new MuteQuack()); mallard.performQuack(); } } ``` # 디자인 원칙 ## 디자인 원칙 1 애플리케이션에서 달라지는 부분을 찾아내고, 달라지지 않는 부분과 분리 - 바뀌는 부분은 따로 뽑아서 **캡슐화** - 나중에 바뀌지 않는 부분에는 영향을 미치지 않고 그 부분만 고치거나 확장 가능 ## 디자인 원칙 2 구현보다는 **인터페이스**에 맞춰서 프로그래밍 하기(상위 형식에 맞춰서 프로그래밍 하기) 여기서 말하는 **인터페이스** 는 *자바의 인터페이스* 가 아닌 인터페이스라는 개념을 지칭한다. 핵심은 실제 실행 시에 쓰이는 객체가 코드에 고정되지 않도록 상위 형식(super type)에 맞춰 프로그래밍해서 다형성을 활용해야 한다는 점이다. 그리고 "상위 형식에 맞춰서 프로그래밍하기" 는 "변수를 선언할 때 보통 추상 클래스나 인터페이스 같은 상위 형식으로 선언해야 한다." 는 의미 이다. ## 디자인 원칙 3 상속 보다는 구성을 활용한다. "A에는 B가 있다." 관계를 생각해보면, 각 오리에는 FlyBehavior 와 QuackBehavior 가 있으며, 각각 나는 행동과 꽥꽥 거리는 행동을 위임 받는다. 이런 식으로 두 클래스를 합치는 것을 **'구성(Composition) 을 이용한다.'** 라고 부른다. # 참조 - [헤드 퍼스트 디자인 패턴](https://product.kyobobook.co.kr/detail/S000001810483) Previous Post [프로그래머스] Lv.1 - K 번째 수 Next Post [디자인 패턴] 옵저버 패턴(Observer Pattern)