Design Pattern
01. Strategy Pattern
kangwook
2022. 3. 12. 23:41
객체가 할 수 있는 행위들 각각을 전략으로 만들어 놓고, 동적으로 행위의 수정이 필요한 경우 전략을 바꾸는 것 만으로 행위의 수정이 가능하도록 만든 패턴
Strategy Pattern
Strategy Pattern은 알고리즘의 그룹들을 정의하고 각 그룹을 캡슐화하며 상호 호환되게 한다. Strategy는 알고리즘을 사용하는 클라이언트와 독립적으로 알고리즘을 변화시킨다.
- Context : Strategy Pattern을 이용하는 역할 수행. 동적으로 구체적인 Strategy(전략)를 바꿀 수 있도록 setter 메서드를 제공할 수도 있음
- Strategy : 인터페이스나 추상 클래스로 외부에서 동일한 방식으로 알고리즘을 호출하는 방법 명시
- ConcreteStrategy : Strategy에서 명시한 알고리즘을 실제로 구현한 클래스
- 캡슐화
- Strategy들은 캡슐화 되어 실제 구현 내용은 ConcreteStrategy에 감추어 은닉
- Context는 사용할 알고리즘을 인터페이스를 이용하여 캡슐화하고, 필요에 따라 동적으로 변경하여 사용할 수 있음
- Strategy를 구현하는 알고리즘들은 해당 계열 안에서 상호 교체가 가능
Strategy Pattern 예제
- 여러 종류의 오리를 특징에 따라 Strategy 패턴을 이용해서 분류
- Strategy를 이용하는 Duck 클래스
- Duck의 행동에 대한 Strategy인 FlyBehavior, QuackBehavior
- Context : Duck클래스
- Duck 클래스는 추상 클래스로 이를 상속받는 여러 하위 클래스(ex. MallardDuck, RubberDuck 등)들이 Strategy 패턴의 context가 됨
public abstract class Duck {
QuackBehavior quackBehavior;
FlyBehavior flyBehavior;
public Duck() {}
public abstract void display();
public void performQuack() {
quackBehavior.quack();
}
public void perfomrFly() {
flyBehavior.fly();
}
}
public class MallardDuck extends Duck {
public MallardDuck() {
quackBehavior = new Quack();
flyBehavior = new FlyWithWings();
}
public void display() {
System.out.println("I'm a real Mallard duck");
}
}
- Strategy : FlyBehavior, QuackBehavior
- Duck 클래스가 이용할 알고리즘을 호출하는 방법이 있는 인터페이스
- Strategy와 이를 구현하는 ConcreteStrategy가 존재
public interface FlyBehavior {
public void fly();
}
public class FlyWithWings implements FlyBehavior {
public void fly() {
System.out.println("I'm flying!!");
}
}
public class FlyNoWay implements FlyBehavior {
public void fly() {
System.out.println("I can't fly");
}
}
public interface QuackBehavior {
public void quack();
}
public class Quack implements QuackBehavior {
public void quack() {
System.out.println("Quack");
}
}
public class MuteQuack implements QuackBehavior {
public void quack() {
System.out.println("<<Silence>>");
}
}
public class Squeak implements QuackBehavior {
public void quack() {
System.out.println("Squeak");
}
}
- Main : MiniDuckSimulator
public class MiniDuckSimulator {
public static void main(Stirng [] args) {
Duck mallard = new MallardDuck();
mallard.performQuack();
mallard.performFly();
}
}
출력결과
Quack
I'm flying!!
추가적인 내용
- Context에 Setter메서드를 추가해서 동적으로 사용하는 방법
- 언제든지 Context가 원하는 Strategy로 코드 수정없이 변경이 가능함
- Duck 클래스에 setter클래스를 추가해주고 이를 상속받는 하위클래스를 선언
public abstract class Duck {
...
public void setFlyBehavior(FlyBehavior fb) {
flyBehavior = fb;
}
public void setQuackBehavior(QuackBehavior qb) {
quackBehavior = qb;
}
}
public class RubberDuck extends Duck {
public RubberDuck() {
flyBehavior = new FlyNoWay();
quackBehavior = new MuteQuack();
}
public void display() {
System.out.println("I'm a rubber duck");
}
}
- Main 클래스에서 실행
public class MiniDuckSimulator {
public static void main(String [] args) {
Duck rubber = new RubberDuck();
rubber.performFly();
rubber.performQuack();
rubber.setFlyBehavior(new FlyWithWings());
rubber.performFly();
}
}
출력결과
I can’t fly
<<Silence>>
I’m flying!!
결론
- Strategy 패턴은 행위 패턴(Behavioral Pattern) 중 하나로 알고리즘을 별도로 분리해서 실행 중에 Context가 사용할 알고리즘을 선택할 수 있게 하는 디자인 패턴
- 행위에 대한 상속 보다는 분리 후 합성을 하는 것이 디자인 원칙에 맞는 좋은 방법
- 장점
- Context 코드의 변경 없이 새로운 Strategy를 추가할 수 있음 → Context에 대한 OCP원칙을 만족
OCP(Open-Close Principle) : “확장에 대해서는 열려 있고 수정에 대해서는 닫혀 있어야 한다.”
- 단점
- 사용하는 Strategy가 적다면 복잡성만 늘어나게 됨
- Strategy 객체에 전달된 인자의 일부가 사용되지 않을 수 있어 통신 오버헤드가 클 수 있음 → 통신 오버헤드 : 통신을 하기 위해 들어가는 간접적인 처리 시간, 메모리 등
본문은 Head First Design Pattern (2004) 를 참고하여 공부하고 작성하였음