top bar

글 목록

레이블이 템플릿 메서드 패턴인 게시물을 표시합니다. 모든 게시물 표시
레이블이 템플릿 메서드 패턴인 게시물을 표시합니다. 모든 게시물 표시

2014년 7월 12일 토요일

템플릿 메서드 패턴 (template method pattern) 2 - 후크(hook)

후크(hook)는 추상 클래스에서 선언되는 메서드긴 하지만 기본적인 내용만 구현되어있거나, 아무 코드도 들어 있지 않은 메서드 이다. 이렇게 하면 서브 클래스 입장에서는 다양한 위치에서 알고리즘에 끼어들 수 있다. 물론 그냥 무시하고 넘어갈수도 있지만 ㅎ

* (이전 포스트 http://asuraiv.blogspot.kr/2014/07/template-method-pattern-1_9.html 를 읽어야 아래 설명들을 이해할수 있다)

아래 CaffeineBeverage 클래스의 음료에 첨가물을 추가하는 addCondiments() 의 호출을, 고객이 원할때만 호출될 수 있도록 조건문을 새로 만들었다. 호출여부는 customerWantsCondisments() 메서드의 리턴값(boolean)이 결정한다.

기본적으로 customerWantsCondiments 메서드는 true를 리턴하지만 서브클래스에서 오버라이드 함으로써 얼마든지 조건을 변경할 수 있다. 

이제 후크를 활용하여 서브클래스를 개선해보자.
아래는 Coffee 클래스에 후크를 활용한 예이다.

후크를 활용하여 Coffee 클래스를 개선했다. 수퍼클래스 템플릿 메서드인 prepareReceipe 메서드의 각 알고리즘 단계중 addCondiments 메서드의 수행 여부를 서브클래스에서 결정하기 위해 customerWantsCondiments 를 오버라이드 했다.

물론 첨가물을 넣을지 말지는 고객이 결정해야 하므로, 입력값을 받아 그 입력값에 따라 true혹은 false를 리턴하도록 구현 했다.

이제 위 설계를 테스트 하기위한 코드를 작성해보자.


이 테스트 코드를 수행한 결과는 아래와 같다.

hook 적용

이제 hook 라는것이 정확히 어떤 용도로 쓰여야 하는지 정리해 보자

1. 알고리즘에서 필수적이지 않은 부분을 필요에 따라 서브클래스에서 구현하는 경우
2. 템플릿 메소드(위에서는 prepareReceipe메서드)에서 앞으로 일어날일 또는 막 일어난 일에 대해 서브클래스에서 반응할 기회를 제공
3. 서브클래스에 수퍼클래스에서 진행되는 작업에 대한 결정을 내리는 기능을 부여하기 위한 용도 (음료에 첨가물을 넣을건지 말건지)

정리하고 보니 더욱더 유연하고 자유도가 높은 알고리즘을 설계할 수 있을것같다.

템플릿 메서드 패턴은 이정도로 마친다.

2014년 7월 9일 수요일

템플릿 메서드 패턴 (template method pattern) 1

템플릿 메소드 패턴을 한마디로 정의하자면
'알고리즘의 캡슐화' 라고한다. 잠깐, '전략 패턴'에도 알고리즘의 캡슐화라는 말을 사용했었 던것 같다. 하지만 무슨 차이가 있을까?

아래 Coffe 클래스가 있다

prepareRecipe 라는 메소드에는 '레시피'인 만큼 커피를 만드는 법(메서드)들이
순서대로 나열되어 있다.

또 아래는 Tea 클래스이다


마찬가지로 prepareRecipe 안에 차를 만드는 메서드들이 나열되어있다.

Coffee와 Tea 이 두가지 클래스는 매우 유사하다. 코드의 중복도 보인다.
딱 눈에 띄는것은 물을 끓이는 boilWater메서드와 컵에 따르는 pourInCup 메서드이다.
이러한 공통적인 메서드를 수퍼클래스로 끌어올리고 prepareReceipe는 수퍼클래스에서
추상 메서드로 선언해보자

그러한 작업을 거치면 1차적으로 아래와 같이 추상화를 할 수 있다.

추상화를 적용한 UML 다이어그램

하지만 이것으로 끝난 것일까? Coffe와 Tea의 메서드들을 가만히 보자.

먼저 brewCoffeCrinds 메서드와 steepTeaBag 메서드는 따지고보면 같다. 커피를 우려내는 것과 티백을 물에 넣어서 홍차를 우려내는 것은 어쨌든 '우려내는' 것은 똑같다.

addSugarAndMilk 메서드와 addLemon 메서드도 마찬가지다. 음료에 무언가를 '추가(add)' 하는것은 똑같지 않은가? 이제 서브 클래스에서 prepareReceipe 메서드를 일일히 구현할 필요없이 추상화 할수 있게 될것 같다.

위의 공통점을 묶어서 수퍼클래스인 CaffeineBeverage 클래스를 재구성하면 다음과 같다.


일단 음료를 '우려내는' brew 메서드를 선언했다. 물론 우려내는 동작은 다르므로 서브클래스에서 구현할 수 있게 추상메서드로 선언했다. 음료에 무언가를 '추가하는' addCondiments도 같은 논리로 추상메서드로 선언하였다.

이렇게 되면 Coffee와 Tea 클래스에는 brew 메서드와 addCondiments 메서드만 용도에 맞게 구현하면 되므로 아래와 같이 간략 해질 것이다.


최종적인 UML 다이어그램은 아래와 같다.

템플릿 메서드 패턴을 적용한 UML 다이어그램


이것이 바로 템플릿 메서드 패턴의 기본이다. 정확히 정리 해야 하기때문에 아래의
prepareReceipe 메서드를 살펴보자.

final void prepareReceipe() {
    boilWater();
    brew();
    pourInCup();
    addCondiments();
}

커피를 만들던, 차를 만들던 우리는 이 prepareReceipe 메서드를 가지고 만들 수 있다.
각각 CaffeineBeverage 클래스를 상속받은 구상클래스의 인스턴스를 생성하여, prepareReceipe 메서드를 호출해주면 된다. 바로 prepareReceipe 메서드가 틀(template)의 역할을 하는것이다.

다음과 같다.

ea myTea = new Tea();
myTea.prepareReceipe();

각각 서브클래스의 인스턴스로 자신이 상속한 수퍼클래스의 템플릿인 prepareReceipe 메서드를 호출하여 음료를 만들어 낸다. 하지만 일부 단계 (brew, addCondiments) 는 서브클래스의 구현에 의존하는 것이다.

이것이 템플릿 메서드 패턴인데... 데체 무엇이 장점일까? 비교해보자

Legacy
Template Method Pattern
Coffee와 Tea가 각각 작업을 처리한다. 두 클래스에서 각자의 알고리즘을 수행
CaffeineBeverage 클래스에서 작업을 처리한다. 알고리즘을 혼자 독점하고 있다
Ceffe와 Tea에 중복된 코드가 있다
CaffeineBeverage 덕분에 서브클래스에서 코드를 재사용 할 수 있다.
알고리즘이 바뀌면 서브클래스를 일일이 열어서 고쳐야 한다
알고리즘은 한군데 모여있기 때문에 그 부분만 고치면 된다
클래스 구조상 새로운 음료를 추가하려면 꽤많은 비용이 든다
prepareReceipe 라는 프레임워크(템플릿)을 제공함으로써 새로운 음료를 추가해도 몇가지 메서드만 추가하면 된다
알고리즘에 대한 지식과 구현 방법이 여러 클래스에 분산되어 있다.
CaffeineBeverage 클래스에 알고리즘에 대한 지식이 집중되어 있으며 일부 구현만 서브 클래스에 의존한다.

.....

템플릿 메서드 패턴을 대규모 프로젝트에 적용한다면 유지보수의 봄이 찾아올것만 같다.

다시한번 정확히 정리해 보자

템플릿 메서드 패턴 : 메서드에서 알고리즘에 골격을 정의한다. 알고리즘의 여러 단계중 일부는 서브클래스에서 구현 할 수 있다. 템플릿 메서드를 이용하면 알고리즘의 구조는 그대로 유지하면서 서브클래스에서 특정 단계를 재정의 할 수 있다.

일반화된 템플릿 메서드 패턴


아, 처음에 언급한 '전략패턴' 과의 차이를 간단하게 고찰해본다면, 일단 전략 패턴은 각각의 행동(알고리즘)을 나타내는 인터페이스를 구현하여 '군(群)'을 형성한다. 그 다음으로 객체지향의 대표적인 속성중 하나인 '다형성(Polymorphism)' 을 이용하여 실행시간에 캡슐화된 알고리즘을 바꿔가며 유연하게 사용할 수 있었던 패턴이었다.

따라서 '알고리즘의 캡슐화' 라는 면에서는 템플릿 메서드 패턴과 비슷하지만, 구현 방법은 전혀 다르다. 템플릿 메서드 패턴에서 말하는 알고리즘 이라는것은 앞의 예에서 보았듯 prepareReceipe 메서드와 같은 일종의 '틀'이다. 그 알고리즘 단계에서 유연하게 바뀔수 있는 부분을 서브클래스에 의존하는것 뿐이다.

전략패턴에서의 캡슐화는 핵심 로직을 캡슐화 하는것이고, 템플릿 메서드 패턴의 캡슐화는 일련의 알고리즘 단계들을 캡슐화 하는 것이다. 정리하고 보니 꽤 큰 차이가 있는것같다.

음.. 일단 다음으로 템플릿 메서드와 함께 사용되는 후크(Hook) 메서드라는것이 있다는데,
나중에 정리해야겠다.