Introduction
우리나라말로 전략(strategy)라고 부르는 이 패턴은 GoF의 분류에서 보면 '행위'에 속합니다.
우선 코드를 통해 생각해 보고, 설명하도록 하겠습니다.
간단한 ATM Machine 을 만들어 봅시다. 조건은 TCP 방식과 UDP 방식으로 은행 DB서버와 통신해야 합니다.
※ 코드가 다소 부실하거나, 억지스러울 수 있으나 요점은 패턴에 대한 이해이기 때문에 너그러히 봐 주시기 바랍니다.
ATM Machine 만들기
패턴은 생각치 않고, 만들어 봅시다.
C와 같이 절차지향 언어만 하다가 객체지향을 처음 접한 사람이 흔히들 만드는 바보같은 코드입니다.
(물론 함수 포인터를 이용하면 얼마든지 해결 할 수 있습니다 ^-^)
TCP로 할 것인지 UDP로 할 것인지 Flag 를 하나 두고, 이에 따라서 동작 방식을 변경하도록 만들었습니다. 물론 동작은 이상없이 잘 될 것입니다.
프로젝트의 요구사항 변경
"TCP, UDP 이외에 회사 제체 개발한 WIT 방식으로 통신이 가능하도록 추가해 주세요" 라는 요구사항을 전달 받았다고 가정합시다.
위 코드는 어떻게 수정/추가 되어야 할까요?
현재 구조로는 Flag 에 WIT 를 추가하고(bool 이 아닌 enum을 써야 겠지요), 모든 함수에 if 조건문을 추가해야만 합니다.
잠시 생각해 보죠. 이런 방식으로 조건이 계속 하나 둘 추가된다면 어떤 불편함/문제점이 생기게 되나요?
그렇습니다. 매번 이미 작성해 놓은 코드를 수정/추가 해야 하는 번거로움이 발생합니다. 그럼 어떻게 해야 할까요?
객체지향에 '상속'을 사용하면 되겠군요. 코드를 변경해 봅시다.
상속을 이용해 봅시다
우선 통신방식에 대한 부분을 제외하고 나머지 부분을 ATMMachine 이라는 부모 클래스로 만들어 봅시다.
그리고 통신에 따라 이를 상속받는 자식 클래스를 만들면 될 것입니다.
이렇게 한다면 새로운 WIT 통신방식을 추가하더라도 기존 코드를 변경할 필요가 없겠군요.
그런데 또 다른 문제가 발생하였습니다.
통신 방식을 매 순간 마음대로 변경할 수 있도록 해 주세요
위와 같은 요구사항이 들어왔습니다.
지금의 방법으로는 동적으로 통신 방식을 변경한다는게 불가능 합니다. 왜냐면 위에 코드에는 생략한 수많은 부모 클래스의 상태(Login 등)들을 유지하기 힘드니까요. 단순히 통신 방식만을 변경하면 되는데, 수 많은 문제점들이 발생하게 됩니다.
이제 패턴을 적용해 봅시다.
StrategyPattern
패턴을 적용해 보겠습니다.
자, 이제 새로운 WIT 통신방식 뿐만 아니라 새로운 통신 방식을 추가하더라고, 기존 완성된 코드는 전혀 손을 댈 필요가 없어졌습니다.
왜냐면 INetwork 라는 Interface 를 구현하는 클래스를 새로 만들어 주기만 하면 되니까요.
이렇게 필요한 '행동'에 대한 부분만 따로 때어서 개발하면, 유지보수의 상당한 비용을 줄일 수 있게 됩니다.
Pattern 정리
1. 함수의 '행동(동작)'이 실시간 적으로 변경되어야 할 경우
게임을 만든다고 가정해 봅시다. 게임속 유닛은 자신이 들고 있는 무기(권총, 칼, 주먹 등)에 따라서 공격 방식이 바뀝니다. 호출하는 쪽에서는 Attack() 을 호출하지만, 행동 방식이 변경되야 한다는 것입니다. 이럴 경우 Weapone 이라는 Interface 를 만들고, 각각의 무기는 이를 구현하면 될 것입니다.
2. 상속 구조에서 '행동(동작)'을 재사용 할 경우
상속은 객체지향 프로그래밍에서 필수적인이며, 코드의 재사용을 하기 위해서 많이 사용됩니다.
A, B, C 에서 같은 동작을 P클래스로 빼내고, A, B, C가 이를 상속받는 구조로 구현하지요.
다음 상황을 생각해 봅시다. Move 라는 abstract method 가 부모클래스 P에 추가되었습니다. A, B는 '걸어서' 이동을 해야하고, C는 '차를 타고' 이동한다고 합시다.
지금과 같은 구조에서는 A, B에 같은 코드가 작성되겠죠. 코드의 재사용을 할 수 없습니다. 재사용을 하기 위해서라면 클래스를 추가로 하나 더 만들어 P와 A,B 사이에 끼워 넣거나, 다른 방법을 써야 합니다.
이때 동작 Move 에 대해서 Interface로 만들고, 이를 상속받은 Walk, Vehicle 을 만들어 사용한다면 쉽게 해결 할 수 있습니다.
3. 조건문을 없애기 위해서
정확히 기억은 나지 않지만, 마틴 파울러의 리팩토링이라는 책에 보면 조건문은 구조를 통해서 충분히 없앨 수 있다고 되어있습니다. 다시말해 복잡한 if / else 문이나 switch 문이 있다면, 이것은 설계가 잘못 되었다는 것입니다. 이때 strategy 패턴을 사용하면 얼만든지 이 문제를 해결 할 수 있습니다.