전략 패턴
여러 알고리즘들을 캡슐화하고 상호 교환 가능하게 만드는 패턴
- 컨텍스트에서 사용할 알고리즘들을 클라이언트에서 선택한다.
즉, 특정 업무를 수행하는 방법이 여러가지 일 경우 여러가지 방법들을 각각의 클래스로 캡슐화하면 캡슐화된 것을 공통 인터페이스로 추상화하여 업무를 수행하는 Client 에서는 공통 인터페이스를 사용하게 하여 Client 코드는 변경되지 않고도 여러 방법들을 바꿔 낄 수 있다.
- Context : 원래 로직을 수행하던 클래스
- ConcreteStrategy : 각각의 알고리즘들이 구현된 클래스
- Strategy : 추상화된 알고리즘 수행 인터페이스
전략 패턴 적용 전
"무궁화 꽃이 피었습니다" 를 실행하는 프로그램이 있다.
이 게임은 "무궁화 꽃이" 까지는 파란불이며, "피었습니다." 까지는 빨간불로 구현을 하였고 여기에 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)
- 상속 대신 위임을 사용할 수 있다.
- 런타임에 전략을 변경할 수 있다.
단점
- 복잡도가 증가한다.
- 클라이언트 코드가 구체적인 전략을 알아야 한다.
실무에선 어떻게 쓰이나?
자바의 Comparator
public class StrategyInJava {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(2);
numbers.add(1);
numbers.add(3);
numbers.add(5);
numbers.add(4);
Collections.sort(numbers, new Comparator<Integer>() {
@Override // ConcreteStrategy
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
}
}
Spring 의 ApplicationContext 등등
public class StrategyInSpring {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext();
ApplicationContext applicationContext1 = new FileSystemXmlApplicationContext();
ApplicationContext applicationContext2 = new AnnotationConfigApplicationContext();
BeanDefinitionParser parser;
CacheManager cacheManager; // 다양한 캐시 전략을 제공
}
}
참고
'디자인 패턴' 카테고리의 다른 글
비지터 패턴 (Visitor Pattern) (0) | 2022.08.13 |
---|---|
템플릿 메서드 패턴 (Template Method Pattern) (0) | 2022.08.13 |
상태 패턴 (State Pattern) (0) | 2022.08.13 |
메멘토 패턴 (Memento Pattern) (0) | 2022.08.12 |
중재자 패턴 (Mediator pattern) (0) | 2022.08.11 |