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): typeConcreteClass1+ field: type# abstractMethod(type): typeConcreteClass2+ field: type# hookMethod(type): type# abstractMethod(type): typetemplateMedthod:{ this->baseMethod(); this->hookMethod(); this->abstractMethod();}baseMethod:{ // do some base logics}hookMethod:{// default}abstract: virtual void abstractMethod() = 0;ExtendsExtends \ 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