Template method Pattern

tm1

1. 개요

템플릿중에 문자 모양으로 구멍이 뚫려있는 얇은 플라스틱이 있다. 그 구멍에 내가 원하는 펜을 이용하여 그리면 반듯한 글씨들을 따라서 그리듯이 쓸 수 있다. 템플릿의 구멍을 보면 어떠한 문자인지는 알 수 있지만 실제로 어떤 문자가 될 지는 내가 사용하는 필기구를 통해서 결정 된다. 같은 문자라고 해도 펜을 사용하면 펜을 사용한 문자가 되고 연필을 쓰면 연필을 사용한 문자가 될 것이다.

tm1

 

더 쉬운 예를 들어 보자. 집을 만든다고 생각 해보자. 집을 만들기 위해선 ‘바닥’, ‘기둥’, ‘벽’, ‘창문’, ‘천장’ 이렇게 필요하다고 정의를 하고 이 필요한 것들은 우리는 만들어야 한다. 하지만 우리가 콘크리트를 사용하여 만들면 콘크리트 집이 될 것이고, 나무를 이용해서 지으면 나무로 만든 집이 될 것이다.

tm0

 

정리하면 다음과 같다. 부모 클래스에서 어떠한 틀이나 골격을 만들고(추상 메소드) 그 부모를 상속받아 구현하게 되는 구현체 들에서 상세 기능이나 모듈을 구현 하면서 구체적인 처리를 결정하고 정의 하는 것이다.

하위클래스에서 어떤 구현을 하더라도 부모 클래스에서 만들어진 틀이나 골격에서 정의된 흐름을 따라 가기 때문에 부모 클래스에서 결정한 대로 자식 클래스에서 이루어 진 다는 것이다.

 

2. 예제 및 설명

Template method 패턴을 이용한 예제 프로그램을 살펴 볼 것이다. 설명 할 예제 프로그램은 ‘문자나 문자열을 5회 반복해서 표시’하는 간단한 프로그램 이다.

tm2

 

AbstractDisplay : open, print, close라는 추상 메소드와 display라는 메소드를 가진 추상 부모 클래스.

open, print, close : AbstractDisplay를 상속한 클래스에서 구현하게 될 추상 메소드.

display() : 자식 클래스에서 구현 하게 될 추상 메소드를 내부적으로 호출하는 메소드. final로 선언되어 자식 클래스에서 재 정의(override) 할 수 없다.

 

CharDisplay : AbstractDisplay 추상 클래스를 구현한 자식 클래스. 문자 하나를 입력받아 처리 한다.

만약 CharDisplay 인스턴스를 만들고 display메소드를 통해서 ‘H’라는 문자를 출력하게 하면 다음과 같이 출력 될 것이다.

<<HHHHH>>

 

StringDisplay : AbastractDisplay 추상 클래스를 구현한 자식 클래스. 문자열을 입력받아 처리 한다.

만약 StringDisplay 인스턴스를 만들고 “Hello, World!” 라는 문자열을 전달한 뒤 display() 메소드를 출력하면 결과는 다음과 같을 것 이다.

 

 

정리 해 봅시다.

AbstractDisplay 에서 어떠한 기능들을 추상적으로 명시하고 어떠한 결정된 메소드 내에서 이 기능 메소드들을 필요에 의해서 호출 하게 된다. AbstractDisplay를 구현한 자식 클래스에서 기능 메소드들을 구현만 하면 되는 형태인 것 이다.

이런 경우 부모 클래스에서 이미 어떠한 알고리즘 로직이나 흐름을 구현해 놓은것 이기 때문에 하위 자식 클래스에서는 해당하는 알고리즘을 다시 재 작성하는 노력을 하지 않아도 된다.

만약 TemplateMethod 패턴을 사용하지 않고 만들게 되면 일일히 모든 클래스에 알고리즘 로직을 작성 하게 된다. 하지만 만약 이 알고리즘 에 버그가 있어 수정을 하게 된다면? 그러면 모든 클래스에 접근하여 해당하는 로직들을 모두 수정하는 노력을 해야 하게 되는 것이다. 이는 확실히 비 효율적이며 불편하기 짝이 없다. TemplateMethod패턴을 이용하여 작성 하게 되면 부모클래스만 수정 하면 되기 때문에 유지-보수의 장점이 생기는 것을 알 수 있다.

하지만, 자식 클래스에서는 부모 클래스와 끈끈한 관계를 취하여 구동되기 때문에 부모클래스에서 언제 추상클래스(자식에서 구현한) 들을 호출하는지 잘 알고 있어야 한다. 이미 부모 클래스에서는 해당하는 알고리즘이 구술되어 그대로 작동 하기 때문에 자식에서 잘못 작성 하게 되면 문제가 생길 수도 있다.

또한, AbstractDisplay를 상속받은 CharDisplay, StringDisplay 클래스 등은 AbstractDisplay변수에 대입할 수 있다. 이때 instanceof 등으로 자식 클래스의 종류를 특정하지 않아도 프로그램이 작동하도록 만드는 것이 좋다.

“부모 클래스형의 변수에 자식 클래스의 어떠한 인스턴스를 대입해도 제대로 작동할 수 있도록 한다”는 원칙은 The liskov substitution principle(LSP)(1) 이라고 불린다.

 

한가지 예를 더 들어보겠다. 이것은 내가 만든 안드로이드의 Dialog를 상속받아 서 만든 추상 클래스의 예 이다. (SwDialog)

SwDialog라는 추상 클래스는 2개의 추상 메소드를 가지고 있다.  2개의 메소드를 initDialog()라는 다이얼로그 초기화 메소드에서 호출 함을 알 수 있다. 다이얼로그 에서 사용될 레이아웃 resource의 id를 세팅 자식 클래스에서 세팅 하도록 하는 추상 메소드와, dialog의 컬러 스타일을 업데이트 해주는 메소드 이다.

안드로이드의 다이얼로그는 거의 비슷비슷한 기능과 디자인을 가지고 있다. 타이틀 텍스트와 내부 메시지, 그리고 취소, 확인 버튼 등 이다. 이러한 일정한 기능들에 대한 세팅, 관리 하는 메소드들을 부모에서 관리하고 자식 클래스에서는 뷰의 레이아웃 리소스ID와 필요한 경우 기능 메소드들을 재정의 하여 사용하게 하려는게 주 목적이다.

또한 미리 정의된 flat 컬러들의 컬러 템플릿 셋을 만들어서 적용시키고 selector나 여러 drawable을 추가로 관리하면서 관리하기 편한 안드로이드 위젯 라이브러리를 만드려 했었었다. 비록 시간이 부족하여 진행을 하지 못하고 있지만..

 

이 SwDialog를 구현한 자식 클래스의 예를 보자.

2개의 메소드가 구현 되어 있음을 알 수 있다. 이미 다이얼로그의 초기화 및 설정 과정은 부모 클래스에서 정의 되어 있다.

 

이러한 예제는 TemplateMethod 패턴의 사용 예 중 하나라고 할 수 있다. 이 외에도 많은 곳에서 사용 하는 패턴이기도 하며 사용 하고 나면 편리함을 많이 느낄 수 있다.

 

 

(1) : Robert C. Martin C++ report, march 1996

참고 : Java언어로 배우는 디자인 패턴 입문

You may also like

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.