컴퓨터 프로그래밍(computer programming)에서 policy pattern이라고도 알려져 있는 strategy pattern은 런타임 시에 알고리즘(algorithms)을 선택할 수 있는 특별한 디자인 패턴(software design pattern) 중 하나이다. 형식적으로 말하면 strategy 패턴은 알고리즘 군(family)을 정의하고, 각각을 캡슐화해서 서로 바꿔가며 쓸 수 있게 한다. strategy는 그를 사용하는 클라이언트 코드로부터 알고리즘을 독립적으로 바꿀 수 있게 해준다.
예를 들어, 어떤 데이터의 유효성을 체크하는 클래스가 있는데, 이 클래스는 데이터의 타입이나 사용자의 선택 등의 특정 인자에 따라 유효성 체크 알고리즘을 선택하기 위해 strategy 패턴을 사용할 수 있다. 이러한 인자는 실제 그 상황이 되기 전까지는 아무 것도 모르고, 그 상황마다 유효성 체크 알고리즘이 완전히 달라질 수도 있다.
유효성을 체크하는 객체로부터 분리되어 캡슐화되는 유효성 체크 전략(strategies)은 시스템의 다른 영역에서 코드 중복없이 사용될 수 있다 .
프로그래밍 언어(programming language)에서 필수적인 요소는 어떤 자료 구조 내의 코드에 대한 참조를 가지고 있다가 필요할 때 사용하는 것이다. 이는 function pointer, first-class function, 객체지향 프로그래밍 언어에서 클래스나 클래스 인스턴스, 또는 reflection을 통한 언어 구현의 내부 코드에 대한 접근같은 메커니즘으로 성취할 수 있는 부분이다.
구조 Structure
예 Example
다음은 Java의 한 예이다.
// The classes that implement a concrete strategy should implement this. // The Context class uses this to call the concrete strategy. interface Strategy { int execute(int a, int b); } // Implements the algorithm using the strategy interface class ConcreteStrategyAdd implements Strategy { public int execute(int a, int b) { System.out.println("Called ConcreteStrategyAdd's execute()"); return a + b; // Do an addition with a and b } } class ConcreteStrategySubtract implements Strategy { public int execute(int a, int b) { System.out.println("Called ConcreteStrategySubtract's execute()"); return a - b; // Do a subtraction with a and b } } class ConcreteStrategyMultiply implements Strategy { public int execute(int a, int b) { System.out.println("Called ConcreteStrategyMultiply's execute()"); return a * b; // Do a multiplication with a and b } } // Configured with a ConcreteStrategy object and maintains a reference to a Strategy object class Context { private Strategy strategy; // Constructor public Context(Strategy strategy) { this.strategy = strategy; } public int executeStrategy(int a, int b) { return strategy.execute(a, b); } } // Test application class StrategyExample { public static void main(String[] args) { Context context; // Three contexts following different strategies context = new Context(new ConcreteStrategyAdd()); int resultA = context.executeStrategy(3,4); context = new Context(new ConcreteStrategySubtract()); int resultB = context.executeStrategy(3,4); context = new Context(new ConcreteStrategyMultiply()); int resultC = context.executeStrategy(3,4); } }
Strategy versus Bridge
strategy 패턴의 UML 클래스 다이어그램은 Bridge 패턴의 다이어그램과 같다. 하지만 이 둘은 의도(목적, intent)가 다르다. strategy 패턴은 행위(behavior)와 관련있지만, Bridge 패턴은 구조(structure)와 관련있다.
context와 strategy간의 커플링이 Bridge패턴의 abstraction와 implementation간의 커플링보다 더 강하다.
Strategy and open/closed principle
strategy 패턴에 따르면, 클래스의 행위는 상속되는 것보다는 인터페이스를 이용해서 캡슐화되어야 한다. 예를들어 car클래스를 생각해보자. car의 두 가지 가능한 기능은 정지(brake)와 가속(accelerate)이다.
정지와 가속은 자동차 모델에 따라 자주 바뀔 수 있는 부분이기 때문에, 이를 구현하기 위한 일반적인 접근은 상속이다. 이 방법은 중대한 단점이 있다. 가속과 정지는 새로운 자동차 모델마다 새로 정의되어야 한다. 이는 자동차 모델의 개수가 늘어날 수록 관리해야하는 부분이 많아지고, 모델간의 코드 중복되 많아진다. 게다가 각각의 모델에 코드를 추가하지 않기도 어렵다.
strategy 패턴에서는 상속 대신에 집합(aggregation)을 이용한다. strategy패턴에서 행위(behaviors)는 여러 인터페이스와 그 인터페이스를 구현하는 클래스들로 정의된다. 각 클래스는 이 인터페이스를 캡슐화한다. 이는 행위와 그 행위를 사용하는 클래스 사이의 커플링을 감소시킨다. 행위는 그 행위를 사용하는 클래스의 변화없이 바뀔 수 있고, 심한 코드 변화없이도 행위를 서로 바꿀 수 있다. 행위는 디자인(설계) 시점 뿐만 아니라 런타임 시에도 바뀔 수 있다. 예를 들어, car 객체의 정지 행위는 brakeBehavior 멤버를 다음과 같이 바꿈으로써 BrakeWithABS()에서 Brake()으로 바뀔 수 있다.
brakeBehavior = new Brake();
이는 설계에서 엄청난 유연성을 제공하고, "클래스는 확장에 열려있어야 하고 수정에는 닫혀있어야 한다"고 말하는 Open/Closed Princicle(OCP)와 잘 어울린다.
'Design Patterns > 영문 위키(Wikipedia)' 카테고리의 다른 글
Template method pattern (0) | 2012.07.05 |
---|---|
State pattern (0) | 2012.07.03 |
Observer pattern (0) | 2012.06.27 |
Memento pattern (0) | 2012.06.20 |
Mediator pattern (0) | 2012.06.19 |