오르카의 아틀리에



책에서도 있는 내용이지만, 템플릿 메소드 패턴은 문자 모양을 따라 구멍이 뚫려있는 얇은 플라스틱판에 비유되곤 한다. 전체적인 골격을 잡아두고, 그것을 연필로 쓰면 연필로 만든 문자, 붓으로 쓰면 붓으로 만든 문자가 된다는 식의 비유다. 사실 처음에는 이런 비유가 확 와 닿지는 않았다. (사실 지금도 적절한 비유인지 잘 모르겠다.) 하여튼 템플릿 메소드 패턴은 "전체적인 메소드(알고리즘)의 흐름(레이아웃)을 부모 클래스에서 정해두고, 자식 클래스에서 자세한 내용을 구현하여 처리하는 패턴"이라고 할 수 있다.


UML

개인적으로 앞에서 책에서 설명했던 비유나 말로는 이 패턴의 의도를 파악하기 조금 그랬기 때문에, 이번에는 먼저 코드를 보고 설명을 하고자 한다. (사실 다른 것도 이렇게 하는 게 맞긴 한데) 먼저 UML을 간단하게 보고 소스 코드를 자세하게 보도록 하자.




지금까지 본 UML 중 가장 단순한 형태이다. 자세히 보면 부모 클래스는 추상으로 선언되어있고, method[num]들은 자식 클래스에 Override 되어있지만 templateMethod는 그렇지 않다. 이유는 이미 method[num]을 이용하여 templateMethod의 처리 흐름을 정의해 두었기 때문이다. 템플릿 메소드 패턴은 이렇게 사용될 주요 메소드(알고리즘)의 처리 흐름을 다른 부품 메소드나 코드의 조합으로 만들어두고, 부품 메소드를 하위 클래스에서 구체적으로 구현함으로써 확장할 수 있게 만드는 패턴이다. 감이 잡혔다면, 코드를 천천히 보자.


소스 코드

차를 만드는 예제를 만들어 보았다. 편의상 디테일한 구현은 문자열 출력으로 대체하였으며 다음과 같다.



천천히 살펴보면 먼저 부모 클래스로 BeverageMachine 이 자리 잡고 있다. 여기서 주석으로 적어놨듯이 Template Method는makeBeverage()라는 메소드이다. 잔을 준비하고 뜨거운 물을 따른 뒤 차를 우려내는 순서대로 흘러가게 레이아웃을 짰다. 필요한 부품 메소드들은 각각 virtual 키워드를 이용하여 만들었고 부모 클래스에서 구현하지 않았다.


자식 클래스는 CoffeeMachine과 BlackTeaMachine이 있는데 각각 부모의 virtual 메소드들을 오버라이드하고 있다.


특별한 상황 & 다른 상황?!

소스에서 보여 주었듯이 부모 클래스에서 템플릿 메소드의 전체적인 레이아웃을 잡고, 자식 클래스에서 템플릿 메소드를 구성하는 부품 메소드를 구체적으로 구현하여(오버라이드하여) 사용하고 있다. 그럼 만약 여러 자식 클래스중에 한 클래스만 중간에 다른 행동을 더 하게 하고 싶으면 어떨까? 물론 그런 상황이 발생하면 좋지는 않겠지만, 만약 발생한다면 2가지 방법을 사용할 수 있다. 클래스 UML을 분리하거나 Hooking 함수를 하나 만들어서 사용하는 것이다. hooking을 사용할지에 관한 변수나 메소드를 만들고, hooking에 관한 내용을 정의할 함수를 부모 클래스에 미리 만들어두어 hooking이 필요한 클래스만 적절하게 오버라이드한 뒤 사용할 수 있게 만드는 방법이다.


어느 방법을 사용하는지는 클래스 설계자가 할 일이다. 편하거나 상황에 따라 더 적절해 보이는 해결방법을 사용하면 된다. 분리해서 관리해도 문제가 없으면 분리하여 관리하는 것이고, 분리하면 곤란하거나 hooking이 필요한 클래스들이 생각보다 많으면 hooking 메소드를 만들어 사용하면 된다.