[Design Pattern] 템플릿 메소드 패턴

Template Method Pattern

템플릿 메소드 패턴이란 객체의 연산에는 알고리즘의 뼈대만 정의하고 각 단계에서 수행할 구체적 처리는 서브클래스쪽으로 미루는 패턴이다. 이를 통해 알고리즘의 구조 자체는 그대로 놔둔 채 알고리즘 각 단계의 처리를 서브클래스에서 재정의할 수 있게 한다.

활용성

  • 어떤 한 알고리즘을 이루는 부분 중 변하지 않는 부분을 한 번 정의해 놓고 다양해질 수 있는 부분은 서브클래스에서 정의할 수 있도록 해야할 때
  • 서브 클래스 사이의 공통적인 행동을 추출하여 공통 클래스에 몰아둠으로써 코드 중복을 피하고 싶을 때

템플릿 메소드 패턴 사용에 따른 결과

서브클래스의 확장 제어

공통 클래스에 훅메소드를 통해 서브 클래스가 어떤 특정한 시점에 해당 훅 연산을 호출하도록 정의함으로써 특정 시점에만 확장할 수 있도록 제어할 수 있다.

코드 재사용

공통되는 코드를 공통 클래스에 명시한 후 달라지는 부분을 서브클래스에 명시함으로 써 코드의 재사용이 가능해진다.

연산의 특성 명시

공통 클래스를 정의할 때 해당 함수가 공통으로 구현해야 하는 추상 연산인지 상황에 따라 구현해야 하는 훅 연산인지 구분해 두어야 한다.

템플릿 메소드

구조

템플릿 메소드의 구조는 다음과 같다.

Template Method Pattern Diagram

C++ 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void TemplateMethodPattern::TemplateMethodPattern()
{
getCoffee();
getTea();
}

void TemplateMethodPattern::getCoffee()
{
Drinks* coffee = new Coffee();
coffee->prepareDrinks();
}

void TemplateMethodPattern::getTea()
{
Drinks* tea = new Tea();
tea->prepareDrinks();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Drinks
{
void boilWater()
{
std::cout<<"Boiling Water..."<<std::endl;
}

//하위 클래스에서 반드시 재정의해야 하는 알고리즘 함수
virtual void brew() = 0;

void pouringDrinks()
{
std::cout<<"Pouring Drinks..."<<std::endl;
}

//상황에 따라 서브 클래스에서 구현 가능한 훅 함수
virtual bool needExtraToppings()
{
return false;
}

virtual void addExtraToppings() {}

virtual void preparedDrink() = 0;
public:
~Drinks() {}

void prepareDrinks()
{
boilWater();
brew();
pouringDrinks();
if(needExtraToppings())
addExtraToppings();
preparedDrink();
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Coffee : public Drinks
{
void brew() override
{
std::cout<<"Brewing Coffee Beans..."<<std::endl;
}

//훅 함수의 구현
bool needExtraToppings()
{
std::cout<<"Need Extra Toppings?"<<std::endl;
std::string input = getInput();
return input == "YES";
}

void addExtraToppings()
{
std::cout<<"Add Some Milk..."<<std::endl;
}

std::string getInput()
{
std::string input;
std::cin>>input;
return input;
}

void preparedDrink() override
{
std::cout<<"Your Coffee is Ready!!"<<std::endl<<std::endl;
}
};
1
2
3
4
5
6
7
8
9
10
11
12
class Tea : public Drinks
{
void brew() override
{
std::cout<<"Brewing Tea..."<<std::endl;
}

void preparedDrink() override
{
std::cout<<"Your Tea is Ready!!"<<std::endl<<std::endl;
}
};

java 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Client {
public static void main(String[] args) {
getCoffee();
getTea();
}

private static void getCoffee() {
Drinks coffee = new Coffee();
coffee.prepareDrinks();
}

private static void getTea() {
Drinks tea = new Tea();
tea.prepareDrinks();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public abstract class Drinks {
public final void prepareDrinks()
{
boilWater();
brew();
pouringDrinks();
if(needExtraToppings())
addExtraToppings();
preparedDrink();
}

private void boilWater()
{
System.out.println("Boiling Water...");
}

//하위 클래스에서 반드시 재정의해야 하는 알고리즘 메소드
protected abstract void brew();

private final void pouringDrinks()
{
System.out.println("Pouring Drinks...");
}

//상황에 따라 서브 클래스에서 구현 가능한 훅 메소드
protected boolean needExtraToppings()
{
return false;
}

protected void addExtraToppings() {}

protected abstract void preparedDrink();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Coffee extends Drinks{
@Override
protected void brew() {
System.out.println("Brewing Coffee Beans...");
}

//훅 메소드의 구현
@Override
protected boolean needExtraToppings() {
System.out.println("Need Extra Toppings?");
String input = getInput();
return input.equals("YES");
}

private String getInput() {
Scanner scanner = new Scanner(System.in);
String input = scanner.next();
return input;
}

@Override
protected void addExtraToppings() {
System.out.println("Add Some Milk...");
}

@Override
protected void preparedDrink() {
System.out.println("Your Coffee is Ready!!");
}
}
1
2
3
4
5
6
7
8
9
10
11
public class Tea extends Drinks{
@Override
protected void brew() {
System.out.println("Brewing Tea...");
}

@Override
protected void preparedDrink() {
System.out.println("Your Tea is Ready!!");
}
}
Author: Song Hayoung
Link: https://songhayoung.github.io/2020/08/30/Design%20Pattern/TemplateMethodPattern/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.