전략 패턴 ( Strategy Pattern )
객체들이 할 수 있는 행위 각각에 대해 전략 클래스를 생성하고, 유사한 행위들을 캡슐화 하는 인터페이스를 정의하여,
객체의 행위를 동적으로 바꾸고 싶은 경우 직접 행위를 수정하지 않고 전략을 바꿔주기만 함으로써 행위를 유연하게 확장하는 방법을 말합니다.
간단히 말해서 객체가 할 수 있는 행위들 각각을 전략으로 만들어 놓고, 동적으로 행위의 수정이 필요한 경우 전략을 바꾸는 것만으로 행위의 수정이 가능하도록 만든 패턴입니다.
- Strategy: 인터페이스나 추상 클래스로 외부에서 동일한 방식으로 알고리즘을 호출하는 방법을 명시
- ConcreateStrategy1, 2, 3: 스트래티지 패턴에서 명시한 알고리즘을 실제로 구현한 클래스
- Context: 스트래티지 패턴을 이용하는 역할 수행. 필요에 따라 동적으로 구체적인 전략을 바꿀 수 있도록 setter 메서드 제공
전략 패턴 적용 전
"무궁화 꽃이 피었습니다" 를 실행하는 프로그램이 있다.
이 게임은 "무궁화 꽃이" 까지는 파란불이며, "피었습니다." 까지는 빨간불로 구현을 하였고 여기에 Speed 라는 필드를 정의하여 "무궁화 꽃이" 혹은 "피었습니다." 를 말하는 속도를 조절 할 수 있도록 하는 기능을 넣었다.
BlueLightRedLight
public class BlueLightRedLight {
private int speed;
public BlueLightRedLight(int speed) {
this.speed = speed;
}
public void blueLight() {
if (speed == 1) {
System.out.println("무 궁 화 꽃 이");
} else if (speed == 2) {
System.out.println("무궁화 꽃이");
} else if (speed == 3){
System.out.println("무광꼬치");
}
}
public void redLight() {
if (speed == 1) {
System.out.println("피 었 습 니 다.");
} else if (speed == 2) {
System.out.println("피었습니다.");
} else if (speed == 3){
System.out.println("펴씀다");
}
}
}
문제점
speed 에 따라 분기를 나눠야하고 다르게 행동해야 한다.
또한 다르게 행동하기 위해 Client 에서 Speed 를 1, 2, 3 과 같은 구체적인 숫자를 바꿔줘야하고 새로운 Speed 4 가 생기면 기존 코드를 수정해야 하는 문제가 발생한다.
이를 전략 패턴을 이용하여 해결해보자.
전략 패턴 적용 후
먼저 Strategy 인터페이스를 정의한다.
// Strategy
public interface Speed {
void blueLight();
void redLight();
}
ConcreteStrategy 를 구현하자.
Slow, Normal, Faster 를 정의하여 이전에 방식의 speed 1, 2, 3 에 해당되는 기능을 구현해준다.
public class Slower implements Speed {
@Override
public void blueLight() {
System.out.println("무 궁 화 꽃 이");
}
@Override
public void redLight() {
System.out.println("피 었 습 니 다.");
}
}
public class Normal implements Speed {
@Override
public void blueLight() {
System.out.println("무궁화꽃이");
}
@Override
public void redLight() {
System.out.println("피었습니다.");
}
}
public class Faster implements Speed {
@Override
public void blueLight() {
System.out.println("무광꼬치");
}
@Override
public void redLight() {
System.out.println("펴씀다.");
}
Context 인 BlueLightRedLight 는 아래와 같다.
// Context
public class BlueLigthRedLight {
private Speed speed;
public BlueLigthRedLight(Speed speed) {
this.speed = speed;
}
public void blueLight() {
speed.blueLight();
}
public void redLight() {
speed.redLight();
}
}
생성자에서 Speed 타입 클래스 (Slower, Normal, Faster) 중 하나를 받아 그에 따르는 로직을 처리한다.
Client 는 아래와 같이 호출 할 수 있다.
public class Client {
public static void main(String[] args) {
BlueLigthRedLight blueLigthRedLight = new BlueLigthRedLight(new Slower());
blueLigthRedLight.blueLight();
blueLigthRedLight.redLight();
}
}
만약, BlueLight 와 RedLight 의 속도를 다르게 하고 싶으면 생성자가 아닌 메소드에서 Speed 타입 클래스를 인자로 받으면 된다.
public class BlueLigthRedLight {
public void blueLight(Speed speed) {
speed.blueLight();
}
public void redLight(Speed speed) {
speed.redLight();
}
}
public class Client {
public static void main(String[] args) {
BlueLigthRedLight blueLigthRedLight = new BlueLigthRedLight();
blueLigthRedLight.blueLight(new Slower());
blueLigthRedLight.redLight(new Faster());
}
}
장점과 단점
장점
- 새로운 전략을 추가하더라도 기존 코드를 변경하지 않는다. (OCP)
- 상속 대신 위임을 사용할 수 있다.
- 런타임에 전략을 변경할 수 있다.
단점
- 복잡도가 증가한다.
- 클라이언트 코드가 구체적인 전략을 알아야 한다
Script
객체가 할 수 있는 행위들을 각각 전략으로 만들어 놓고, 동적으로 행위의 수정이 필요한 경우 전략을 바꾸는 것만으로 행위의 수정이 가능하도록 하는 패턴 이다. 기존 코드를 변견하지 않고 상속 대신 위임을 할수 있다는 장점이 있다. 단점으로는 복잡도가 늘고 클라이언트 코드가 구체적인 전략을 알아야한다
'디자인패턴' 카테고리의 다른 글
책임 연쇄 패턴(Chain of Responsibility Pattern) (0) | 2022.10.08 |
---|---|
템플릿 메소드 패턴(Template Method Pattern) (1) | 2022.09.25 |
플라이웨이턴 패턴 (Flyweight Pattern) (0) | 2022.08.15 |
퍼사드 패턴(Facade Pattern) (0) | 2022.08.14 |
데코레이터(Decorator) 패턴 (0) | 2022.08.04 |