-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathConceptualExample.cpp
More file actions
162 lines (132 loc) · 5.55 KB
/
Copy pathConceptualExample.cpp
File metadata and controls
162 lines (132 loc) · 5.55 KB
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// ===========================================================================
// ConceptualExample.cpp // Template Method
// ===========================================================================
#include <memory>
#include <print>
namespace ConceptualExample_Template_Method_Pattern {
/**
* The Abstract Class defines a template method that contains a skeleton of some
* algorithm, composed of calls to (usually) abstract primitive operations.
*
* Concrete subclasses should implement these operations, but leave the template
* method itself intact.
*/
class AbstractClass {
public:
AbstractClass() = default; // Base class should not be directly instantiable
virtual ~AbstractClass() = default;
AbstractClass(const AbstractClass&) = delete;
AbstractClass& operator=(const AbstractClass&) = delete;
AbstractClass(AbstractClass&&) = delete;
AbstractClass& operator=(AbstractClass&&) = delete;
/**
* The template method defines the skeleton of an algorithm:
* Non-virtual: Subclasses must not redefine the algorithm.
*/
void TemplateMethod() const
{
BaseOperation1(); // overriden, but it's not mandatory
RequiredOperation1(); // MUST be overriden
if (ShouldExecuteBranch()) { // MUST be overriden
BaseOperation2(); // overriden, but it's not mandatory
Hook1(); // overriden, but it's not mandatory
}
else
{
RequiredOperation2(); // MUST be overriden
BaseOperation3(); // overriden, but it's not mandatory
Hook2(); // overriden, but it's not mandatory
}
}
/**
* These operations already have implementations.
*/
protected:
virtual void BaseOperation1() const {
std::println("AbstractClass says: I am doing the bulk of the work");
}
virtual void BaseOperation2() const {
std::println("AbstractClass says: But I let subclasses override some operations");
}
virtual void BaseOperation3() const {
std::println("AbstractClass says: But I am doing the bulk of the work anyway");
}
/**
* These operations have to be implemented in subclasses.
*/
virtual bool ShouldExecuteBranch() const = 0;
virtual void RequiredOperation1() const = 0;
virtual void RequiredOperation2() const = 0;
/**
* These are "hooks." Subclasses may override them, but it's not mandatory
* since the hooks already have default (but empty) implementation.
* Hooks provide additional extension points in some crucial places of the
* algorithm.
*/
virtual void Hook1() const {} // default: do nothing
virtual void Hook2() const {} // default: do nothing
};
/**
* Concrete classes have to implement all abstract operations of the base class.
* They can also override some operations with a default implementation.
*/
class ConcreteClass1 final : public AbstractClass {
protected:
bool ShouldExecuteBranch() const override {
std::println("ConcreteClass1 says: Implemented ShouldExecuteBranch decision");
return true;
}
void RequiredOperation1() const override {
std::println("ConcreteClass1 says: Implemented required Operation1");
}
void RequiredOperation2() const override {
std::println("ConcreteClass1 says: Implemented required Operation2");
}
};
/**
* Usually, concrete classes override only a fraction of base class' operations.
*/
class ConcreteClass2 final : public AbstractClass {
protected:
bool ShouldExecuteBranch() const override {
std::println("ConcreteClass2 says: Implemented ShouldExecuteBranch decision");
return false;
}
void RequiredOperation1() const override {
std::println("ConcreteClass2 says: Implemented required Operation1");
}
void RequiredOperation2() const override {
std::println("ConcreteClass2 says: Implemented required Operation2");
}
void BaseOperation1() const override {
std::println("ConcreteClass2 says: Overridden BaseOperation1");
}
void Hook1() const override {
std::println("ConcreteClass2 says: Overridden Hook1");
}
};
/**
* The client code calls the template method to execute the algorithm. Client
* code does not have to know the concrete class of an object it works with, as
* long as it works with objects through the interface of their base class.
*/
static void clientCode(const AbstractClass& obj) {
// ...
obj.TemplateMethod();
// ...
}
}
void test_conceptual_example() {
using namespace ConceptualExample_Template_Method_Pattern;
std::println("Same client code can work with different subclasses (1):");
ConcreteClass1 concreteObject1;
clientCode(concreteObject1);
std::println();
std::println("Same client code can work with different subclasses (2):");
ConcreteClass2 concreteObject2;
clientCode(concreteObject2);
std::println();
}
// ===========================================================================
// End-of-File
// ===========================================================================