템플릿 메서드 패턴이란?
알고리즘의 구조를 부모 클래스에서 정의하고, 구체적인 단계는 서브클래스에서 구현하는 행동 디자인 패턴
특징
- 부모 클래스의 템플릿 메서드가 전체 알고리즘 흐름 제어
- 일부 단계는 추상 메서드로 선언(순수 가상 함수)해서 자식 클래스에서 강제 구현 필요
- 코드 중복 최소화 및 확장성 향상
예시
1. 추상 부모 클래스
#include <iostream>
#include <string>
using namespace std;
// 추상 부모 클래스
class Beverage {
public:
// 템플릿 메서드: 전체 과정을 정의
void prepareRecipe() {
boilWater(); // 공통 단계
brew(); // 자식 클래스에서 구현
pourInCup(); // 공통 단계
addCondiments(); // 자식 클래스에서 구현
}
// 공통 메서드
void boilWater() {
cout << "물을 끓입니다.\n";
}
void pourInCup() {
cout << "컵에 따릅니다.\n";
}
// 자식 클래스에서 구현해야 할 추상 메서드
virtual void brew() = 0;
virtual void addCondiments() = 0;
virtual ~Beverage() = default; // 가상 소멸자
};
추상 부모 클래스에서는 전체적인 흐름을 짜고 모든 애들이 다 똑같이 쓸 공통 메서드는 정의해줍니다.
// 자식 클래스 1: Coffee
class Coffee : public Beverage
{
public:
void brew() override
{
cout << "커피를 내립니다.\n";
}
void addCondiments() override
{
cout << "설탕과 우유를 추가합니다.\n";
}
};
// 자식 클래스 2: Tea
class Tea : public Beverage
{
public:
void brew() override
{
cout << "차를 우려냅니다.\n";
}
void addCondiments() override
{
cout << "레몬을 추가합니다.\n";
}
};
자식클래스에서는 순수 가상함수로 무조건 구현해야겠다고 추가한 부분을 구현해줍니다.
커피와 Tea로 나눠서 각 각의 자식 클래스에서 하나는 커피를 내리고 하나는 차를 우려내는 메서드를 넣었다.
메인함수
// 메인 함수
int main() {
cout << "커피 만드는 중:\n";
Coffee coffee;
coffee.prepareRecipe();
cout << "\n차 만드는 중:\n";
Tea tea;
tea.prepareRecipe();
return 0;
}
메인함수에서는 이제 각 타입의 객체 하나씩 만들어 같은 매서드를 실행해 보겠다.
이렇게 입력받은 값에 따라 잘 나온다.
*여태까지 배운 행동패턴 비교* (여기가 제일 중요함)
특징 | 템플릿 메서드 패턴 | 전략 패턴 | 상태 패턴 |
목적 |
알고리즘 구조 정의 및 세부 구현 위임
|
알고리즘을 동적으로 교체
|
상태에 따라 동작 변경
|
구현 방식 |
상속 기반
|
구성(composition) 기반
|
구성 기반
|
유연성 |
낮음 (상속에 의존)
|
높음 (런타임에 전략 변경 가능)
|
높음 (상태 전환 가능)
|
동작 변경 시점 |
컴파일 타임 (클래스 정의 시 고정)
|
런타임
|
런타임
|
코드 재사용 |
부모 클래스에서 공통 로직 재사용
|
전략 객체 간 독립적
|
상태 객체 간 독립적
|
사용 예시 |
공통 프로세스에 세부 변형 필요
|
결제 방식 선택
|
자판기, 게임 캐릭터 상태
|
복잡도 |
비교적 단순
|
전략 객체 증가 시 복잡
|
상태 객체 증가 시 복잡
|
그래서 어떻게 뭘 쓸지 판단하면 좋을까?
-
템플릿 메서드 패턴은 단순하고 고정된 프로세스에 적합.
-
전략 패턴은 동적인 알고리즘 교체가 필요한 경우 유리.
-
상태 패턴은 객체의 상태 변화에 따라 동작을 분리하고 싶을 때 적합.