전략 패턴(Strategy Pattern)
알고리즘 군을 정의하고 캡슐화해주며, 서로 언제든지 바꿀 수 있도록 해주는 방법이다.
즉, 객체가 해야 하는 행위들을 각각 전략으로 만든 후, 동적으로 행위의 수정이 필요한 경우 전략을 바꿔주는 것만으로 수정하는 패턴이다.
왜 사용하는가?
예시를 들고, 이를 통해 전략 패턴을 왜 사용하는지 알아보자.
![[디자인 패턴] 전략 패턴(Strategy Pattern) - undefined - 왜 사용하는가? [디자인 패턴] 전략 패턴(Strategy Pattern) - undefined - 왜 사용하는가?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
이처럼 Duck 클래스가 있다고 하자.
이 오리 클래스 안에는 quack 메서드와 swim 메서드, display 메서드가 있다.
이 Duck 클래스를 상속받는 MallardDuck과 RubberDuck 클래스 또한 울음소리를 낼 수 있고, 수영을 할 수 있다.
만약 여기서 Duck 클래스에 fly 메소드를 추가시킨다면 어떨까?
![[디자인 패턴] 전략 패턴(Strategy Pattern) - undefined - 왜 사용하는가? [디자인 패턴] 전략 패턴(Strategy Pattern) - undefined - 왜 사용하는가?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
RubberDuck 클래스는 고무오리를 뜻하는 클래스이므로 fly 메서드가 적절하지 않다.
하지만 Duck 클래스를 받았기 때문에 fly 메소드를 갖게 되는 것이다.
이를 해결하기 위해, RubberDuck 클래스에서 fly 메서드를 빈 메서드로 정의하거나, 재정의하여 개별적으로 해결할 수 있다.
하지만 이는 LSP(Lisvoke Substitution Principle)와 ISP(Interface Segregation Principle)에 위배되고, 코드를 수정하지 않으면 행위가 수정되지 않고, 코드를 수정해야만 행위를 수정할 수 있으므로 OCP(Open-Closed Principle)를 위배한다.
이러한 문제점을 해결하기 위해 전략 패턴(Strategy Pattern)을 사용한다.
전략 패턴(Strategy Pattern) 구현
위에서 예시로 들었던 Duck 클래스를 전략 패턴을 사용해보자.
각 오리들은 울음소리가 다르고, 날 수 있는지도 다르기에 두 기능을 오리 클래스에서 분리시켜 각각의 전략으로 선언한다.
두 전략을 구현하는 방법이 거의 동일하므로 fly 전략만을 보도록 하자.
fly() 메소드는 날 수 있다와 날 수 없다로 나뉘었다.
즉, 두개의 전략 클래스를 만든다.(FlyWithWings, FlyNoWay)
또한 이 두 전략을 캡슐화해주기 위해 FlyStrategy 인터페이스를 생성한다.
![[디자인 패턴] 전략 패턴(Strategy Pattern) - undefined - 전략 패턴(Strategy Pattern) 구현 [디자인 패턴] 전략 패턴(Strategy Pattern) - undefined - 전략 패턴(Strategy Pattern) 구현](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
이를 코드로 보면 다음과 같다.
FlyStrategy
public interface FlyStrategy {
void doFly();
}
FlyWithWings
public class FlyWithWings implements FlyStrategy {
@Override
public void doFly() {
System.out.println("i can fly with my wings");
}
}
FlyNoWay
public class FlyNoWay implements FlyStrategy {
@Override
public void doFly() {
System.out.println("I can't fly");
}
}
이제 오리에 대한 클래스를 정의하자.
오리 클래스에는 display 메소드와 전략을 설정할 setFlyStrategy 외에 울음소리 전략을 설정할 setQuackStrategy 등 각 오리들에 공통적으로 필요한 메서드들이 들어있다.
![[디자인 패턴] 전략 패턴(Strategy Pattern) - undefined - 전략 패턴(Strategy Pattern) 구현 [디자인 패턴] 전략 패턴(Strategy Pattern) - undefined - 전략 패턴(Strategy Pattern) 구현](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
위 클래스들을 코드로 봐보자.
Duck
import java.util.Objects;
public abstract class Duck {
private FlyStrategy flyingStrategy;
private QuackStrategy quackStrategy;
public Duck(FlyStrategy flyingStrategy, QuackStrategy quackStrategy) {
setFlyStrategy(flyingStrategy);
setQuackStrategy(quackStrategy);
}
public void setFlyStrategy(FlyStrategy flyingStrategy) {
this.flyingStrategy = Objects.requireNonNull(flyingStrategy);
}
public void setQuackStrategy(QuackStrategy quackStrategy) {
this.quackStrategy = quackStrategy;
}
public void quack() {
quackStrategy.doQuack();
}
public void swim() {
System.out.println("I can swimming");
}
public void fly() {
flyingStrategy.doFly();
}
public abstract void display();
}
MallardDuck
public class MallardDuck extends Duck {
public MallardDuck(FlyStrategy flyingStrategy, QuackStrategy quackStrategy) {
super(flyingStrategy, quackStrategy);
}
@Override
public void display() {
System.out.println("나는 청둥오리");
}
}
RubberDuck
public class RubberDuck extends Duck {
public RubberDuck(FlyNoWay flyNoWay, Squeak squeak) {
super(flyNoWay, squeak);
}
@Override
public void display() {
System.out.println("나는 고무오리");
}
}
Duck 클래스에서 각 전략들을 오리 클래스와 연결하기 위해 의존 관계를 주입해주는 것을 확인할 수 있다.
마지막으로 이 패턴이 잘 돌아가는지 확인해보도록 하자.
import java.util.ArrayList;
import java.util.List;
public class DuckClient {
public static void flySimulation(Duck duck) {
duck.display();
duck.fly();
}
public static void swimSimulation(Duck duck) {
duck.display();
duck.swim();
}
public static void quackSimulation(Duck duck) {
duck.display();
duck.quack();
}
public static void main(String[] args) {
List<Duck> ducks = new ArrayList<>();
Duck duck1 = new MallardDuck(new FlyWithWings(), new Quack());
Duck duck2 = new RedHeadDuck();
ducks.add(duck1);
ducks.add(duck2);
Duck duck3 = new RubberDuck(new FlyNoWay(), new Squeak());
ducks.add(duck3);
System.out.println("오리 수영 시뮬레이션");
for(Duck duck: ducks) swimSimulation(duck);
System.out.println();
System.out.println("오리 날기 시뮬레이션");
for(Duck duck: ducks) flySimulation(duck);
System.out.println();
System.out.println("오리 울음소리 시뮬레이션");
for(Duck duck: ducks) quackSimulation(duck);
System.out.println();
}
}
![[디자인 패턴] 전략 패턴(Strategy Pattern) - undefined - 전략 패턴(Strategy Pattern) 구현 [디자인 패턴] 전략 패턴(Strategy Pattern) - undefined - 전략 패턴(Strategy Pattern) 구현](https://blog.kakaocdn.net/dn/YXgz8/btrN6nN5Ib8/PM83KSH3txxr5o2kqzreM1/img.png)
위와 같이 각 오리의 수영 여부, 날기 여부, 울음소리를 수행시간동안 각각 다르게 출력하는 것을 확인할 수 있다.
전략 패턴(Strategy Pattern)의 장단점
장점
- 위임이라는 느슨한 연결을 통해 전략을 쉽게 바꿀 수 있고, 실행 시간에 행위를 변경할 수 있다.
- 새 전략을 추가하기 쉽다.
- 여러 행위가 전략 패턴으로 구현될 경우, 이들의 조합으로 객체를 쉽게 구성할 수 있다.
단점
- 행위의 모델링이 쉽지 않을 수도 있다
'디자인 패턴 > Design Pattern' 카테고리의 다른 글
[디자인 패턴] 장식자 패턴(Decorator Pattern) (0) | 2022.10.14 |
---|---|
[디자인 패턴] 관찰자 패턴(Observer 패턴) (0) | 2022.10.11 |
댓글