diff --git a/CMakeLists.txt b/CMakeLists.txt index c5e5f1f..b5c1be4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -104,6 +104,7 @@ set(APP_SOURCES "src/patterns/behavioral/Mediator.cpp" "src/patterns/behavioral/Memento.cpp" "src/patterns/behavioral/Visitor.cpp" + "src/patterns/behavioral/TemplateMethod.cpp" ) # Test files diff --git a/docs/uml/pattern_behavioral_templatemethod.drawio.svg b/docs/uml/pattern_behavioral_templatemethod.drawio.svg new file mode 100644 index 0000000..d5d71ce --- /dev/null +++ b/docs/uml/pattern_behavioral_templatemethod.drawio.svg @@ -0,0 +1,4 @@ + + + +
main:
{
AbstractClass* clazz1 = new ConcreteClass1();
clientCode(clazz1);

AbstractClass* clazz2 = new ConcreteClass2();
clientCode(clazz2);
}

clientCode:
{
 clazz->templateMethod();
}
Client
+ clientCode(AbstractClass* clazz): type
<<Abstract>>
AbstractClass
+ field: type
+ templateMethod(type): type
# baseMethod(type): type

# hookMethod(type): type
# abstractMethod(type): type
ConcreteClass1
+ field: type
# abstractMethod(type): type
ConcreteClass2
+ field: type
# hookMethod(type): type
# abstractMethod(type): type

templateMedthod:
{
 this->baseMethod();
 this->hookMethod();
 this->abstractMethod();
}
baseMethod:
{
 // do some base logics
}
hookMethod:
{
// default
}
abstract: 
virtual void abstractMethod() = 0;
Extends
Extends
\ No newline at end of file diff --git a/src/patterns/behavioral/TemplateMethod.cpp b/src/patterns/behavioral/TemplateMethod.cpp new file mode 100644 index 0000000..87a2d32 --- /dev/null +++ b/src/patterns/behavioral/TemplateMethod.cpp @@ -0,0 +1,123 @@ +// cppcheck-suppress-file [functionStatic] + +// is a behavioral design pattern that defines the skeleton of an algorithm in the `superclass` +// but lets `subclasses` override specific steps of the algorithm without changing its structure. +// Appicability: +// (*) when you want to let clients extend only particular steps of an algorithm, but not the whole algorithm or its structure. +// (**) when you have several classes that contain almost identical algorithms with some minor differences. +// As a result, you might need to modify all classes when the algorithm changes. +// (**) when a behavior makes sense only in some classes of a class hierarchy, but not in others. +// UML: docs/uml/patterns_behavioral_templatemethod.drawio.svg + +#include + +namespace +{ + namespace TemplateMethod + { + class AbstractClass + { + public: + virtual ~AbstractClass() = default; + // Template Method (non-virtual => cannot be overridden) + // Defines the algorithm's skeleton and ensures subclasses cannot change the flow. + void templateMethod() const + { + baseOperation1(); + abstractMethod1(); + hookOperation1(); + + baseOperation2(); + abstractMethod2(); + hookOperation2(); + } + + protected: + // 1. Base operations: These have full implementations and cannot be overridden. + void baseOperation1() const + { + // Common logic step 1 + std::cout << "[AbstractClass]\t Executed base operation - 1\n"; + } + + void baseOperation2() const + { + // Common logic step 2 + std::cout << "[AbstractClass]\t Executed base operation - 2\n"; + } + + // 2. Hook methods: Subclasses may override them to extend behavior, but overriding is optional. + virtual void hookOperation1() const {} + virtual void hookOperation2() const {} + + // 3. Abstract methods: Subclasses MUST provide implementations. + virtual void abstractMethod1() const = 0; + virtual void abstractMethod2() const = 0; + }; + + class ConcreteClass1 : public AbstractClass + { + public: + void abstractMethod1() const override + { + std::cout << "[ConcreteClass1]\t Implemented Operation - 2\n"; + } + + void abstractMethod2() const override + { + std::cout << "[ConcreteClass1]\t Implemented Operation - 2\n"; + } + + void hookOperation1() const override + { + std::cout << "[ConcreteClass1]\t Overridden Hook - 1\n"; + } + }; + + class ConcreteClass2 : public AbstractClass + { + public: + void abstractMethod1() const override + { + std::cout << "[ConcreteClass2]\t Implemented Operation - 2\n"; + } + + void abstractMethod2() const override + { + std::cout << "[ConcreteClass2]\t Implemented Operation - 2\n"; + } + }; + + namespace Client + { + void clientCode(const AbstractClass *clazz) + { + clazz->templateMethod(); + } + } + void run() + { + std::cout << "\t[ConcreteClass1] Executed templateMethod\n"; + AbstractClass *clazz1 = new ConcreteClass1(); + Client::clientCode(clazz1); + + std::cout << "\t[ConcreteClass1] Executed templateMethod\n"; + AbstractClass *clazz2 = new ConcreteClass2(); + Client::clientCode(clazz2); + + delete clazz1; + delete clazz2; + } + } +} + +struct TemplateMethodAutoRunner +{ + TemplateMethodAutoRunner() + { + std::cout << "\n--- TemplateMethod Pattern Example ---\n"; + TemplateMethod::run(); + } +}; + +static TemplateMethodAutoRunner instance; \ No newline at end of file