Strategy Pattern에 대해 한번 알아봅시다.
Strategy Pattern의 기본적인 정의는 아래와 같습니다.
알고리즘 군을 정의하고 각각을 캡슐화 하여 교환해서 사용할 수 있도록 만든 패턴으로 알고리즘을 사용하는 클라이언트와는 독립적으로 알고리즘이 변경 가능하다.
말이 쉬우면서도 어렵다.
알고리즘 군을 정의하고 그것을 캡슐화(?)하여 교환해서 사용하도록 만든 것이라고 한다.
이걸 좀 더 쉽게 풀어서 생각하여 보자.
알고리즘 군이란 것이 무엇일까? 우리가 어떠한 클래스를 설계할 때 변경되는 부분이라고 생각하면 쉽다. 클래스 설계 시 기능(Method명)은 같지만 표현방식(구현/알고리즘)이 다른 Method들이 있다. 예를 들어 Animal이라는 클래스가 있고, 이 클래스에 cry라는 method가 있다고 생각해보자. 사자나 호랑이 토끼, 코끼리 등 많은 클래스들이 Animal의 클래스를 상속 받겠지만 모두 울음소리는 다르지 않은가? 울음소리가 없는 동물들도 분명 존재할텐데 그 동물에겐 cry라는 method는 불필요한 method가 될 것이다.
이렇게 method명은 같지만 그 안에 구현하는 방법 즉, 각각의 알고리즘들이 다르다. 이런 알고리즘의 군(모임)을 따로 골라내자.
이렇게 골라낸 알고리즘 군을 캡슐화 하여 해당 기능을 레퍼런스화 하여 사용하도록 하는 것이다.
Strategy Pattern을 사용함으로써 얻을 수 있는 것은?
실행 중에도 기능을 변경할 수 있으며 기능 또는 기능군 추가시 기존 코드 변경 필요 없음
아래 Strategy Pattern의 구조를 한번 봅시다.
-Context : Strategy의 구상객체를 소유하며 해당 구상객체의 method를 실행하는 method 보유
-Strategy : 알고리즘의 추상 객체, 여러 타입의 구상객체들의 추상 객체가 된다.
-ConcreteStrategy A~C : Context에 적용될 각각 다른 알고리즘으로 되있는 Method 소유
좀 더 이해를 돕기 위해 Head First Design Pattern을 참고하여 설명하겠습니다.
당신이 만약 오리에 대한 게임을 제작하게 되었습니다. 제일 처음 당신은 Duck이란 클래스를 만들고 method 로 fly와 quack를 구현할 것입니다. 그리고 다양한 종류의 오리들(예를 들어 청동오리 등등)을 Duck을 상속받아 만들 것입니다.
- public abstract class Duck {
- public Duck(){
- }
- public abstract void display();
- public void fly(){
- }
- public void quack(){
- }
- }
이렇게 만든 Duck 클래스를 상속받아서 사용하고 있습니다. 그런데 회사에서 고무오리를 넣어달라고 한다. 그럼 고무오리의 클래스를 만들고 Duck을 상속 받을 것인가? 하지만 문제가 있다. 고무오리는 fly 하지도 quack 하지도 못합니다. 그럼 그냥 method 안을 비워두면 되지 라고 생각하시는 분들 많으실 겁니다. 물론 그렇게 하더라도 프로젝트에는 지장이 없을 것이다. 물론 이상도 없습니다.
이러한 문제는 코드를 정비하는데 큰 문제를 일으키게 될 것입니다. 외부의 요구사항을 반영하고 수정하면 지금처럼 계속 오버라이드하는 것만으로는 문제를 해결할 수 없다는 것을 알수 있습니다.
좀더 깔끔한 방법을 찾아야 할 것 같습니다. 이때 생각해 보면 위의 Strategy Pattern을 언제 사용할 수 있는지 생각해 보시면 됩니다. 이렇게 지속적으로 변하는 알고리즘 군을 모아서 캡슐화 하는 것입니다.
여기서 디자인 원칙 중 한가지를 말씀드린다면 아래와 같습니다.
애플리케이션에서 달라지는 부분을 찾아 내고, 달라지지 않는 부분으로부터 분리시킨다.
코드에 새로운 요구사항이 있을 때마다 바뀌는 부분이 있다면, 그 행동을 바뀌지 않는 다른 부분으로부터 골라내서 분리해야 한다는 것을 알 수 있습니다. 달라지는 부분을 찾아 캡슐화를 하면 의도치 않은 일이 일어나는 것을 막으면서 시스템의 유연성을 향상 시킬 수 있습니다.
위 UML이 Strategy Pattern을 적용한 것입니다. Duck 클래스에는 공통적으로 변하지 않는 method들로 구성되어 있으며, 변경되는 알고리즘군 여기서는 fly와 quack을 Interface로 만들어 각각의 행동을 정의하도록 하였습니다. 이렇게 되면 flyBehavior 인터페이스를 가진 클래스만 변경을 하거나 추가를 하고, Duck 클래스는 이제 전혀 변경할 필요가 없어졌습니다.
Strategy Pattern은
1. 행위들이 조금씩 다를 뿐 개념적으로 관련된 많은 클래스들이 무수히 많이 존재 하는 경우
2. 알고리즘의 변형이 필요한 경우
3. 사용자가 모르고 있는 데이터를 사용해야 하는 알고리즘이 존재할 경우
4. 많은 행위를 정의하기 위해 클래스 안에 복잡한 다중 조건문을 사용해야 하는 경우
와 같은 경우에 활용하면 됩니다^^
※함께 스터디 중인 김민수, 은희유 님 감사.
'Software Architecture > Design Pattern' 카테고리의 다른 글
[Design Pattern] Iterator Pattern (0) | 2018.12.18 |
---|---|
[Design Pattern] Template Method Pattern (0) | 2018.12.15 |
[Design Pattern] Null Object Pattern (0) | 2017.02.20 |
[Design Pattern] Observer Pattern (0) | 2014.07.19 |
[Design Pattern] Design Pattern 이란? (0) | 2014.07.06 |