Definition
The template method pattern defines a skeleton of an algorithm or an operation, with some steps varying in different subclasses. The template method lets subclasses redefine specific steps in the algorithm without changing the algorithm structure.
There are two types of operation based on if there is a default placeholder for the substitute:
Operation | Notes |
---|---|
Abstract operation | There is no default method, and the subclass must refine it. |
Hook operation | There is a default placeholder, but the subclass can choose to override it. |
Scenario
We want a framework where some codes in their methods vary from case to case. And inside the framework, maybe we want the code to be called in some specific way. We can define a template (an interface class) that has some slots missing and then have others fill in those slots.
Class Diagram
Save
is a method that calls Validate
and SuccessSave
. The interface (Record
) has implemented Save
; some codes in this method can be shared across different cases while some don’t, and some codes must be called in a specific order.
The subclasses can override the slots (Validate
and SuccessSave
) to match their usage. This allows changes in the behavior of an abstract class by introducing a new concrete class, filling in the missing slots, and overriding the slots, but it does not modify the backbone of the underlying method (Open/Closed Principle).
The invariants are in the superclass (the interface Record
), and the variants are in the subclass (User
, Post
).
When combining different pieces, composition is favered over inheritance. Template method pattern uses inheritance, and sometimes this has trouble dealing with multi-inheritance. So use template method unless it has some specific reasons.
Open/Closed principle: Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.
Design pattern is about separating the variant from the invariant.
Used Cases
- Create a framework.
Implementation
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
// template.h
#ifndef __TEMPLATE_H__
#define __TEMPLATE_H__
#include <string>
class Record {
private:
virtual void Validate() = 0; // abstract operation
virtual void SuccessSave(); // hook operation
public:
void Save();
};
class User : public Record {
private:
void Validate() override;
void SuccessSave() override;
};
class Post : public Record {
private:
void Validate() override;
};
#endif //__TEMPLATE_H__
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// template.cpp
#include "template.h"
#include <iostream>
void Record::Save() {
std::cout << "[Record] Saving ..." << std::endl;
Validate();
SuccessSave();
}
void Record::SuccessSave() { std::cout << "[Record] Save successful" << std::endl; }
void User::Validate() { std::cout << "[User] Validate" << std::endl; }
void User::SuccessSave() { std::cout << "[User] Save successful" << std::endl; }
void Post::Validate() { std::cout << "[Post] Validate" << std::endl; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// main.cpp
#include <iostream>
#include "template.h"
int main() {
User user = User();
user.Save();
std::cout << "----------" << std::endl;
Post post = Post();
post.Save();
return 0;
}