Pattern - Strategy
의도
동일 계열의 알고리즘을 정의하고, 각 알고리즘을 캡슐화하며, 이들을 상호교환이 가능하도록 만듭니다.
알고리즘을 사용하는 클라이언트와 상관 없이 독립적으로 알고리즘을 다양하게 변경할 수 있습니다.
동기
텍스트 스트림을 라인으로 구분할 수 있는 알고리즘은 매우 많습니다. 이들 알고리즘을 직접 클래스에 하드코딩하는 것은 다음과 같은 이유로 별로 바람직 하지 않습니다.
- 줄 분리 코드가 사용자 프로그램에 들어가면, 줄 분리가 필요한 사용자 프로그램이 점점 더 복잡해집니다. 크기도 점점 커질 것이고, 유지보수하기도 힘들어집니다. 특히 다중 줄 분리 알고리즘을 지원한다면 더욱 그렇습니다.
- 때에 따라서 필요한 알고리즘이 다르기 때문에 모든 알고리즘을 다 제공할 필요는 없습니다.
- 알고리즘을 구성하는 문장들이 사용자 코드와 합쳐져 있을 때는 새로운 알고리즘을 추가하거나 기존의 것을 다양화하기가 어렵습니다.
이러한 문제는 서로 다른 줄 분리 알고리즘을 하나씩 캡슐화한 클래스를 정의함으로써 피해갈 수 있습니다.
이렇게 캡슐화된 알고리즘을 가리켜 전략 패턴이라고 합니다.
활용성
- 업무 자체는 변경되지 않는데, 업무를 처리하는 담당자가 변경되는 경우
- 예약 시스템에서 Copy라는 기는이 있는데 이를 예약에서 쓸경우와 투숙에서 쓸 경우
- 동사무소에서 신청서를 출력할 때, 신청서의 양식에 따라서 신청서의 내용이 달라질 경우
행동들이 조금씩 다른 뿐 개념적으로 관련된 많은 클래스들이 존재할 때, 전략 패턴은 많은 행동 중 하나를 가진 클래스를 구성할 수 있는 방법을 제공합니다.
-
알고리즘의 변경이 필요할 때. 이를 테면 기억 공간과 처리 속도 간의 절충에 따라 서로 다른 알고리즘을 정의할 수 있을 것입니다. 이러한 변형물들이 알고리즘의 상속 관계로 구현될 때 전략 패턴을 사용할 수 있습니다.
-
사용자가 몰라야 하는 데이터를 사용하는 알고리즘이 있을 때. 노출하지 말아야 할 복잡한 자료 구조는 Strategy 클래스에만 두면 되므로 사용자는 몰라도 됩니다.
-
하나의 클래스가 많은 행동을 정의하고, 이런 행동들이 그 클래스의 연산 안에서 복잡한 다중 조건문의 모습을 취할 때, 많은 조건문보다는 각각을 Strategy 클래스로 옮겨놓는 것이 좋습니다.
항목에 대한 설명
-
Strategy
Compositer
제공하는 모든 알고리즘에 대한 공통의 연산들을 인터페이스로 정의합니다. Context 클래스는 ConcreteStrategy 클래스에 정의한 인터페이스를 통해서 실제 알고리즘을 사용합니다. -
ConcreteStrategy
SimpleCompositer, TeXCompositor, ArrayCompositor
Strategy 인터페이스를 실제 알고리즘으로 구현합니다. -
Context
Composition
ConcreteStrategy 객체를 통해 구성됩니다. 즉, Strategy 객체에 대한 참조자를 관리하고, 실제로는 Strategy 서브 클래스의 인스턴스를 갖고 있음으로써 구체화합니다. 또한 Strategy 가 자료에 접근해가는데 필요한 인터페이스를 제공합니다.
협력방법
- Strategy 클래스와 Context 클래스는 의사교환을 통해 선택한 알고리즘을 구현합니다. 즉, Context 클래스는 알고리즘에 해당하는 연산이 호출되면, 알고리즘 처리에 필요한 모든 데이터를 Strategy 클래스로 보냅니다. 이때, Context 객체 자체를 Strategy 연산에다가 인자로 전송할 수도 있습니다.
- Context 클래스는 사용자 쪽에서 온 요청을 각 전략 객체로 전달합니다. 이를 위해서 사용자는 필요한 알고리즘에 해당하는 ConcreteStrategy 객체를 생성하여 이를 Context 클래스에 전송하는데, 이 과정을 거치면 사용자는 Context 객체와 동작할 때 전달한 ConcreteStrategy 객체와 함께 동작합니다. 사용자가 선택할 수 있는 동일 계열의 ConcreteStrategy 클래스 군이 준비될 때가 자주 있습니다.
결과
- 동일 계열의 관련 알고리즘 군이 생깁니다.
- 서브 클래싱을 사용하지 않는 대안입니다.
- 조건문을 없앨수 있습니다.
- 구현의 선택이 가능합니다.
- 사용자(프로그램)는 서로 다른 전략을 알아야 합니다.
- Strategy 객체와 Context 객체 사이에 의사소통 오버헤드가 있습니다.
- 객체 수가 증가합니다.
코드 예제 – 기본 샘플
package designpattern.gof_strategy.sample01;
public class ServiceProvider {
public static void main(String[] args) {
Strategy strategy = null;
Solider rambo = new Solider();
strategy = new StrategyGun();
rambo.runContext(strategy);
System.out.println("\n");
strategy = new StrategyGrenade();
rambo.runContext(strategy);
}
}
package designpattern.gof_strategy.sample01;
public interface Strategy {
void runStrategy();
}
package designpattern.gof_strategy.sample01;
public class Solider {
void runContext(Strategy strategy){
System.out.println("배틀 그라운드 시작");
strategy.runStrategy();
System.out.println("배틀 종료 ");
}
}
package designpattern.gof_strategy.sample01;
public class StrategyGrenade implements Strategy {
@Override
public void runStrategy() {
System.out.println("수류탄 꽝꽝");
}
}
package designpattern.gof_strategy.sample01;
public class StrategyGun implements Strategy {
@Override
public void runStrategy() {
System.out.println("다다다다다다다다");
}
}