템플릿 메소드 패턴
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.
1. 개요
템플릿 메서드 패턴은 객체 지향 프로그래밍에서 알고리즘의 구조를 정의하는 디자인 패턴이다. 이 패턴은 상위 클래스(AbstractClass)에서 알고리즘의 뼈대(templateMethod)를 정의하고, 하위 클래스(ConcreteClass)에서 알고리즘의 특정 단계를 구현하도록 한다. 이를 통해 코드 중복을 줄이고, 서브클래스가 다양한 동작을 구현하도록 하며, 코드 생성과 함께 사용될 때 유용하게 활용된다. 템플릿 메서드 패턴은 제어 반전의 한 예시이며, 팩토리 메서드 패턴과 관련이 있다.
더 읽어볼만한 페이지
- 메소드 (컴퓨터 프로그래밍) - 소멸자 (컴퓨터 프로그래밍)
소멸자는 객체가 메모리에서 제거되기 직전에 호출되는 멤버 함수로, 객체 자원 해제 및 정리 작업을 수행하며, C++ 등 여러 언어에서 구현되고 메모리 누수 방지에 기여한다. - 메소드 (컴퓨터 프로그래밍) - 동적 디스패치
동적 디스패치는 프로그램 실행 시 호출할 메서드를 결정하는 메커니즘으로, 단일 또는 다중 객체 기준으로 선택하며, 유연성과 확장성을 높이지만 성능 오버헤드를 발생시킬 수 있다. - 소프트웨어 디자인 패턴 - 모델-뷰-컨트롤러
모델-뷰-컨트롤러(MVC)는 소프트웨어 디자인 패턴으로, 응용 프로그램을 모델, 뷰, 컨트롤러 세 가지 요소로 분리하여 개발하며, 사용자 인터페이스 개발에서 데이터, 표현 방식, 사용자 입력 처리를 분리해 유지보수성과 확장성을 높이는 데 기여한다. - 소프트웨어 디자인 패턴 - 스케줄링 (컴퓨팅)
스케줄링은 운영 체제가 시스템의 목적과 환경에 맞춰 작업을 관리하는 기법으로, 장기, 중기, 단기 스케줄러를 통해 프로세스를 선택하며, CPU 사용률, 처리량 등을 기준으로 평가하고, FCFS, SJF, RR 등의 알고리즘을 사용한다.
| 템플릿 메소드 패턴 |
|---|
2. 구조
템플릿 메서드 패턴은 알고리즘의 뼈대를 기반 클래스에 정의하고, 구체적인 처리 내용은 하위 클래스에서 구현하도록 하는 구조를 가진다.
기반 클래스에는 템플릿 메서드가 존재하며, 이 메서드는 알고리즘의 전체적인 흐름, 즉 변하지 않는 로직을 담고 있다.[1] 알고리즘의 특정 단계 중 변경이 필요하거나 구체화되어야 하는 부분은 별도의 메서드(종종 추상 메서드나 기본적인 구현만 가진 메서드) 호출로 남겨둔다.
하위 클래스는 이 기반 클래스를 상속받아, 템플릿 메서드가 호출하는 추상 메서드나 변경 가능한 메서드를 각자의 상황에 맞게 구체적으로 구현(override)한다.[5] 이를 통해 전체 알고리즘의 구조는 그대로 유지하면서 일부 단계의 동작 방식만 변경할 수 있다. 중요한 점은 하위 클래스가 템플릿 메서드 자체를 재정의하지 않는다는 것이다.
이러한 방식은 프로그램 실행 시 상위 수준의 코드가 구체적인 실행 로직을 결정하는 것이 아니라, 하위 수준의 객체(하위 클래스의 인스턴스)에 따라 런타임에 로직이 결정되므로 제어 역전(Inversion of Control, IoC)의 한 형태로 볼 수 있다.
또한, 훅 메서드를 통해 하위 클래스가 필요에 따라 알고리즘의 특정 부분을 선택적으로 확장하거나 수정할 수 있는 유연성을 제공하기도 한다. 훅 메서드는 기반 클래스에 기본적인 (때로는 비어있는) 구현을 가지며, 하위 클래스에서 재정의될 수 있다.
2. 1. UML 클래스 다이어그램
위의 UML 클래스 다이어그램은 템플릿 메서드 패턴의 구조를 나타낸다.
- '''`AbstractClass`''': 템플릿 메서드인 `templateMethod()`를 정의한다. 이 메서드는 알고리즘의 전체적인 구조, 즉 골격을 결정한다. `templateMethod()`는 알고리즘의 변하지 않는 부분을 직접 구현하고, 변하는 부분은 다른 보조 메서드(`primitive1()`, `primitive2()` 등)를 호출하는 방식으로 구성된다. 이 보조 메서드들은 추상 메서드로 선언되거나 기본적인 구현만 제공될 수 있다.
- '''`ConcreteClass`''': `AbstractClass`를 상속받아, 부모 클래스에서 추상적으로 정의되었거나 변경이 필요한 보조 메서드들을 구체적으로 구현한다. 이를 통해 전체 알고리즘의 구조는 유지하면서 특정 단계의 실제 동작을 변경할 수 있다.[5]
`AbstractClass`에서 정의된 추상 메서드들의 가시성이 `protected` (#)인 경우가 많은데, 이는 이 메서드들이 주로 `templateMethod()` 내부에서만 호출되도록 의도되었기 때문이다. 외부에서는 `templateMethod()`만 호출하여 전체 알고리즘을 실행하게 된다.
아래는 템플릿 메서드 패턴을 나타내는 다른 예시 UML 다이어그램이다.

2. 2. 구성 요소
이 패턴은 크게 두 가지 요소로 구성된다.- '''템플릿 메서드''': 기반 클래스(보통 추상 클래스)에 정의된 메서드이다. 이 메서드는 알고리즘의 전체적인 구조, 즉 변하지 않는 부분을 담고 있다. 템플릿 메서드는 알고리즘의 각 단계가 정해진 순서대로 실행되도록 보장한다.[1] 알고리즘 내에서 변경될 수 있는 부분은 별도의 '도우미 메서드'를 호출하는 방식으로 구현된다. 기반 클래스에서는 이 도우미 메서드들에 대해 기본적인 구현을 제공하거나, 아예 구현하지 않고 추상 메서드로 남겨둘 수 있다.
- '''하위 클래스''': 기반 클래스를 상속받는 클래스들이다. 이 클래스들은 템플릿 메서드 내의 비어 있거나 변경 가능한 부분(도우미 메서드)을 각자의 필요에 맞게 구체적으로 구현한다.[5] 중요한 점은 하위 클래스가 템플릿 메서드 자체를 재정의(override)해서는 안 된다는 것이다.
프로그램 실행 시, 특정 하위 클래스의 객체에 템플릿 메서드 실행을 요청하면 알고리즘이 시작된다. 상속에 의해 기반 클래스에 정의된 템플릿 메서드가 먼저 실행된다. 템플릿 메서드가 도우미 메서드를 호출하면, 이 호출은 실제 객체인 하위 클래스에게 전달된다. 만약 하위 클래스에서 해당 도우미 메서드를 재정의했다면 재정의된 버전이 실행되고, 그렇지 않다면 기반 클래스로부터 상속받은 기본 구현이 실행된다. 이 방식을 통해 전체 알고리즘의 흐름은 동일하게 유지하면서도, 세부적인 단계는 어떤 하위 클래스의 객체를 사용하느냐에 따라 다르게 처리될 수 있다.
이러한 작동 방식은 상위 수준의 코드(템플릿 메서드)가 실행할 구체적인 알고리즘을 직접 결정하는 것이 아니라, 런타임에 하위 수준의 알고리즘(하위 클래스의 메서드)이 선택된다는 점에서 제어 역전(Inversion of Control, IoC)의 한 예로 볼 수 있다.
템플릿 메서드 내에서 호출되는 도우미 메서드 중 일부는 '''훅 메서드'''일 수 있다. 훅 메서드는 기반 클래스에 구현되어 있지만, 기본적으로 아무런 동작도 하지 않는 빈 메서드이다. 훅 메서드가 존재하는 이유는 하위 클래스에서 필요에 따라 이 메서드를 재정의하여, 템플릿 메서드 자체를 변경하지 않고도 알고리즘의 특정 부분을 선택적으로 확장하거나 수정할 수 있도록 하기 위함이다. 즉, 변경 가능한 구현을 '걸어둘 수 있는 갈고리(hook)'를 제공하는 셈이다.
아래는 템플릿 메서드 패턴을 나타내는 클래스 다이어그램이다.
'AbstractClass'에서 정의된 추상 메서드들의 접근 제한자가 protected (#)인 이유는 이 메서드들이 주로 'AbstractClass.templateMethod()' 내부에서만 사용될 것으로 예상되기 때문이다.
3. 활용
템플릿 메소드 패턴은 주로 프레임워크 개발과 같이 전체적인 구조는 유지하면서 특정 단계만 변경해야 하는 경우에 유용하게 사용된다. 즉, 알고리즘의 뼈대는 상위 클래스에 정의하고, 구체적인 내용은 하위 클래스에서 구현하는 방식이다. 이는 제어 역전(Inversion of Control)의 한 예시로 볼 수 있다.[1]
템플릿 메소드 패턴을 사용하는 주된 이유는 다음과 같다.
- 유연한 확장: 하위 클래스에서 훅 메소드를 오버라이딩하여 알고리즘의 특정 부분을 쉽게 변경하거나 확장할 수 있다.[5] 이를 통해 각기 다른 요구사항에 맞춰 동작을 다양하게 구현하는 것이 가능하다.
- 코드 중복 감소: 알고리즘의 공통적인 흐름은 추상 클래스의 템플릿 메소드에 한 번만 정의하고, 변경이 필요한 부분만 하위 클래스에서 구현하므로 코드 중복을 효과적으로 줄일 수 있다.
- 구조 유지 및 특수화: 하위 클래스가 템플릿 메소드 자체를 직접 오버라이딩하는 대신, 특정 지점(훅 메소드)만 수정하도록 유도함으로써 전체 알고리즘의 구조는 유지하면서 필요한 부분만 안전하게 특수화할 수 있다. 템플릿 메소드를 직접 수정하면 알고리즘의 흐름 자체가 크게 바뀔 위험이 있지만, 훅 메소드를 이용하면 이러한 위험 없이 세부 동작만 조정 가능하다.
아래는 Java를 이용해 문자열 배열의 서식을 지정하고 목록으로 만드는 템플릿 메소드 패턴 예시이다.
// 추상 클래스: 알고리즘의 뼈대를 정의 (템플릿 메소드: display)
abstract class StringLister {
// 하위 클래스에서 구현해야 할 추상 메소드 (훅 메소드 역할)
protected abstract String formatHeader();
protected abstract String formatItem(String item);
protected abstract String formatFooter();
// 템플릿 메소드: 알고리즘의 전체 흐름을 정의하며, final로 선언하여 오버라이딩 방지
public final String display(String[] items) {
StringBuilder result = new StringBuilder(this.formatHeader());
for (String item : items) {
result.append(this.formatItem(item));
}
result.append(this.formatFooter());
return result.toString();
}
}
// 구체적인 하위 클래스 1: 일반 텍스트 형식으로 목록 생성
class PlainTextStringLister extends StringLister {
@Override
protected String formatHeader() {
return ""; // 헤더 없음
}
@Override
protected String formatItem(String item) {
return " - " + item + "\r\n"; // 각 항목 앞에 '-' 추가
}
@Override
protected String formatFooter() {
return ""; // 푸터 없음
}
}
// 구체적인 하위 클래스 2: HTML 형식으로 목록 생성
class HtmlStringLister extends StringLister {
@Override
protected String formatHeader() {
return "
- \r\n"; // HTML 목록 시작 태그
- " + item + " \r\n"; // 각 항목을
- 태그로 감쌈
}
@Override
protected String formatFooter() {
return "
}
@Override
protected String formatItem(String item) {
return "
}
}
// 실행 예제
public class TemplateMethodTest {
public static void main(String[] argv) {
String[] items = {"First", "Second", "Third"};
StringLister l1 = new PlainTextStringLister(); // 일반 텍스트 리스터
StringLister l2 = new HtmlStringLister(); // HTML 리스터
// 각 리스터의 display 메소드 호출 (템플릿 메소드 실행)
System.out.println(l1.display(items));
System.out.println(l2.display(items));
}
}
위 예제 코드를 실행하면 다음과 같은 결과가 출력된다. `PlainTextStringLister`와 `HtmlStringLister`는 동일한 `display` 템플릿 메소드를 사용하지만, 각 클래스에서 오버라이딩한 `formatHeader`, `formatItem`, `formatFooter` 메소드에 따라 다른 형식의 결과물을 만들어낸다.
- First
- Second
- Third
- First
- Second
- Third
클래스 다이어그램과의 대응 관계는 다음과 같다.
- '''AbstractClass''': `StringLister`
- '''AbstractClass.templateMethod()''': `StringLister.display()`
- '''ConcreteClass''': `PlainTextStringLister`, `HtmlStringLister`
3. 1. 코드 생성기와의 사용
템플릿 패턴은 코드를 자동으로 생성해서 사용할 때 유용하다. 자동 생성된 코드를 다룰 때의 과제는 원본 소스 코드를 변경하면 생성된 코드 역시 변경된다는 점이다. 만약 수작업으로 생성된 코드를 수정했다면, 소스 코드를 다시 생성할 때 이러한 수정 사항들이 사라질 위험이 있다. 그렇다면, 생성된 코드를 어떻게 맞춤 설정해야 할까?템플릿 패턴은 이 문제에 대한 해결책을 제공한다. 만약 생성된 코드가 템플릿 메서드 패턴을 따른다면, 생성된 코드는 모두 추상 슈퍼클래스가 될 것이다. 수작업으로 이루어진 맞춤 설정이 서브클래스에 국한되어 있는 한, 코드 생성기는 이러한 수정 사항을 덮어쓸 위험 없이 다시 실행될 수 있다. 코드 생성과 함께 사용될 때, 이 패턴은 때때로 세대 간극 패턴(Generation gap pattern)이라고 불린다.[7]
4. 예제
템플릿 메소드 패턴은 다양한 프로그래밍 언어로 구현될 수 있다. 아래는 대표적인 예시로, C++와 Java를 이용한 구현 방법을 보여준다. 각 언어별 상세 구현은 해당 하위 섹션에서 확인할 수 있다.
4. 1. Java 예제
다음은 '''템플릿 메서드 패턴'''을 사용하여 문자열 배열을 서식화하고 나열하는 Java 프로그램의 예시이다.abstract class StringLister {
protected abstract String formatHeader();
protected abstract String formatItem(String item);
protected abstract String formatFooter();
public final String display(String[] items) {
StringBuilder result = new StringBuilder(this.formatHeader());
for (String item : items) {
result.append(this.formatItem(item));
}
result.append(this.formatFooter());
return result.toString();
}
}
class PlainTextStringLister extends StringLister {
protected String formatHeader() {
return "";
}
protected String formatItem(String item) {
return " - " + item + "\r\n";
}
protected String formatFooter() {
return "";
}
}
class HtmlStringLister extends StringLister {
protected String formatHeader() {
return "
- \r\n";
- " + item + " \r\n";
}
protected String formatItem(String item) {
return "
}
protected String formatFooter() {
return "
}
}
public class TemplateMethodTest {
public static void main(String[] argv) {
String[] items = {"First", "Second", "Third"};
StringLister l1 = new PlainTextStringLister();
StringLister l2 = new HtmlStringLister();
System.out.println(l1.display(items));
System.out.println(l2.display(items));
}
}
이 샘플을 실행하면 다음 실행 결과가 얻어진다. 처음 3행은
PlainTextStringLister.display()의 반환 값이며, 빈 줄을 사이에 두고 그 다음에 출력되는 것은 HtmlStringLister.display()의 반환 값이다.- First
- Second
- Third
- First
- Second
- Third
참고로, 클래스 다이어그램과의 대응 관계는 다음과 같다.
; AbstractClass
:
StringLister; AbstractClass.templateMethod()
:
StringLister.display(); ConcreteClass
:
PlainTextStringLister, HtmlStringLister4. 2. C++ 예제
다음은 C++14를 기반으로 한 구현 예시이다.[1]```cpp
#include
#include
// 추상 클래스(AbstractClass) 역할을 하는 View 클래스 정의
class View {
public:
// 하위 클래스에서 구현해야 할 추상 기본 연산(primitive operation) 정의
// 여기서는 가상 함수(virtual function)를 사용하여 하위 클래스에서 재정의(override) 가능하도록 함
virtual void doDisplay() {}
// 알고리즘의 뼈대를 정의하는 템플릿 메소드(template method) 구현
// 템플릿 메소드는 기본 연산뿐만 아니라, 추상 클래스에 정의된 다른 연산이나
// 다른 객체의 연산을 호출할 수 있음
void display() {
setFocus(); // 템플릿 메소드의 일부 단계
doDisplay(); // 하위 클래스에서 구현될 단계 호출
resetFocus(); // 템플릿 메소드의 일부 단계
}
// 가상 소멸자(virtual destructor) 정의: 상속 관계에서 메모리 누수 방지
virtual ~View() = default;
private:
// 템플릿 메소드에서 사용되는 일반 메소드
void setFocus() {
std::cout << "View::setFocus\n";
}
// 템플릿 메소드에서 사용되는 일반 메소드
void resetFocus() {
std::cout << "View::resetFocus\n";
}
};
// 구체 클래스(ConcreteClass) 역할을 하는 MyView 클래스 정의
// View 클래스를 상속받음
class MyView : public View {
// 추상 기본 연산(doDisplay)을 구체적으로 구현
void doDisplay() override {
// 뷰의 내용을 그리는(렌더링하는) 코드 구현
std::cout << "MyView::doDisplay\n";
}
};
int main() {
// 스마트 포인터(std::unique_ptr)를 사용하여 MyView 객체 생성 및 관리
// 스마트 포인터는 동적으로 할당된 메모리를 자동으로 해제하여 메모리 누수(memory leak)를 방지함
std::unique_ptr
// 템플릿 메소드 호출
// 실제 실행되는 doDisplay()는 MyView 객체의 구현 버전임
myview->display();
return 0; // main 함수 정상 종료
}
```
위 프로그램의 실행 결과는 다음과 같다.
```text
View::setFocus
MyView::doDisplay
View::resetFocus
5. 관련 패턴
팩토리 메서드 패턴은 내부에 템플릿 메서드 패턴을 포함하는 경우가 많다.
참조
[1]
서적
Design Patterns
Addison-Wesley
[2]
서적
Head First Design Patterns
http://shop.oreilly.[...]
O'REILLY
2012-09-12
[3]
웹사이트
The Template Method design pattern - Structure
http://w3sdesign.com[...]
2017-08-12
[4]
문서
LePUS3 legend
http://lepus.org.uk/[...]
[5]
웹사이트
Template Method Design Pattern
http://sourcemaking.[...]
Source Making - teaching IT professional
2012-09-12
[6]
서적
Pro Objective-C Design Patterns for iOS
Apress
[7]
서적
Pattern Hatching: Design Patterns Applied
http://www.informit.[...]
Addison-Wesley Professional
1998-06-22
[8]
서적
Design Patterns
Addison-Wesley
[9]
서적
Head First Design Patterns
http://shop.oreilly.[...]
O'REILLY
2012-09-12
[10]
웹인용
The Template Method design pattern - Structure
http://w3sdesign.com[...]
2017-08-12
[11]
문서
LePUS3 legend
http://lepus.org.uk/[...]
본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.
문의하기 : help@durumis.com