diff --git a/CMakeLists.txt b/CMakeLists.txt index 0003950..0048dcc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,6 +97,7 @@ set(APP_SOURCES "src/patterns/structural/Composite.cpp" "src/patterns/structural/Flyweight.cpp" "src/patterns/structural/Facade.cpp" + "src/patterns/structural/Decorator.cpp" ) # Test files diff --git a/docs/uml/patterns_structural_decorator.drawio.svg b/docs/uml/patterns_structural_decorator.drawio.svg new file mode 100644 index 0000000..6b9bce1 --- /dev/null +++ b/docs/uml/patterns_structural_decorator.drawio.svg @@ -0,0 +1,4 @@ + + + +Client+ clientCode(IComponent comp): type<<Interface>>IComponent+ operation(Type): TypeConcreteComponentConcreteComponentWithAConcreteComponentWithBConcreteComponentWithABConcreteComponentWithCConcreteComponentWithA1UseExtendsExtendsExtendsExtendsExtendsClient+ clientCode(IComponent* comp): type<<Interface>>IComponent+ operation(Type): TypeUseBaseDecorator+ m_component: *IComponent+ BaseDecorator(IComponent* comp)+ operation(Type): TypeConcreteDecoratorA+ field: type+ ConcreteDecoratorA(IComponent* comp)+ method(type): typeConcreteDecoratorB+ field: type+ ConcreteDecoratorB(IComponent* comp)+ method(type): typeConcreteDecoratorC+ field: type+ ConcreteDecoratorB(IComponent* comp)+ method(type): typeConcreteComponent+ operation(Type): Type1ExtendsExtendsExtendsmethod:{ BaseDecorator::operation(); this->extra();}BaseDecorator(IComponent* comp):m_component{comp}{ // }ConcreteDecoratorA(IComponent* comp):BaseDecorator{comp}{//}IComponent simple = ConcreteComponent();clientCode(&simple);IComponent simpleWithA = ConcreteDecoratorA(simple);clientCode(&simple); \ No newline at end of file diff --git a/src/patterns/structural/Composite.cpp b/src/patterns/structural/Composite.cpp index 602a7e6..9a903e1 100644 --- a/src/patterns/structural/Composite.cpp +++ b/src/patterns/structural/Composite.cpp @@ -4,7 +4,7 @@ // Appicability: // (*) when you have to implement a tree-like object structure. // (**) when you want the client code to treat both simple and complex elements uniformly. -// UML: docs/uml/patterns_structural_facade.drawio.svg +// UML: docs/uml/patterns_structural_composite.drawio.svg #include #include @@ -534,7 +534,7 @@ struct CompositeAutoRuner CompositeAutoRuner() { std::cout << "\n--- Composite Pattern Example ---\n"; - // Problem::run(); + Problem::run(); CompositePattern::run(); } }; diff --git a/src/patterns/structural/Decorator.cpp b/src/patterns/structural/Decorator.cpp new file mode 100644 index 0000000..1d2cd4f --- /dev/null +++ b/src/patterns/structural/Decorator.cpp @@ -0,0 +1,228 @@ +// Decorator is a structural design pattern that lets you attach new behaviors to objects +// by placing these objects inside special wrapper objects that contain the behaviors. +// Appicability: +// (*) when you need to be able to assign extra behaviors to objects at runtime without breaking the code that uses these objects. +// (**) when it’s awkward or not possible to extend an object’s behavior using inheritance. +// UML: docs/uml/patterns_structural_decorator.drawio.svg + +#include +#include +#include +#include + +namespace +{ + namespace Problem + { + + class IComponent + { + public: + virtual ~IComponent() {} + virtual std::string operation() const = 0; + }; + + class ConcreteComponent : public IComponent + { + public: + std::string operation() const override + { + return "ConcreteComponent"; + } + }; + + class ComponentWithA : public ConcreteComponent + { + public: + std::string operation() const override + { + return ConcreteComponent::operation() + " + FeatureA"; + } + }; + + class ComponentWithB : public ConcreteComponent + { + public: + std::string operation() const override + { + return ConcreteComponent::operation() + " + FeatureB"; + } + }; + + class ComponentWithAandB : public ConcreteComponent + { + public: + std::string operation() const override + { + return ConcreteComponent::operation() + " + FeatureA + FeatureB"; + } + }; + + // [P1] + // If you have 3 features , e.g FeatureC -> many combinations + // If you have 5 features -> 32 subclasses + + namespace Client + { + void clientCode(const IComponent &comp) + { + std::cout << comp.operation() << "\n"; + } + } + + void run() + { + std::cout << "\n\nProblem\n"; + IComponent *simple = new ConcreteComponent; + Client::clientCode(*simple); + + IComponent *withA = new ComponentWithA; + Client::clientCode(*withA); + + IComponent *withB = new ComponentWithB; + Client::clientCode(*withB); + + IComponent *withAB = new ComponentWithAandB; + Client::clientCode(*withAB); + + delete simple; + delete withA; + delete withB; + delete withAB; + } + + } + + namespace DecoratorPattern + { + class IComponent + { + public: + virtual ~IComponent() {} + virtual std::string operation() const = 0; + }; + + /** + * Concrete Component + * - is a class of objects being wrapped. + * - defines the basic behavior, which can be altered by decorators. + */ + class ConcreteComponent : public IComponent + { + public: + std::string operation() const override + { + return "ConcreteComponent"; + } + }; + + /** + * The base Decorator class follows the same interface as the other components. + * - has a field for referencing a wrapped object. + * - the field’s type should be declared as the component interface so it can contain both concrete components and decorators. + * - the base decorator delegates all operations to the wrapped object. + */ + class BaseDecorator : public IComponent + { + protected: + IComponent *m_component; + + public: + explicit BaseDecorator(IComponent *component) : m_component{component} {} + + /** + * The Decorator delegates all work to the wrapped component. + */ + std::string operation() const override + { + return m_component->operation(); + } + }; + + /** + * Concrete Decorators : + * - call the wrapped object and alter its result in some way. + * - define extra behaviors that can be added to components dynamically. + * - override methods of the base decorator and execute their behavior either before + * or after calling the parent method. + */ + class ConcreteDecoratorA : public BaseDecorator + { + public: + explicit ConcreteDecoratorA(IComponent *component) : BaseDecorator{component} {} + + std::string operation() const override + { + return BaseDecorator::operation() + " + FeatureA"; + } + }; + + class ConcreteDecoratorB : public BaseDecorator + { + public: + explicit ConcreteDecoratorB(IComponent *component) : BaseDecorator{component} {} + + std::string operation() const override + { + return BaseDecorator::operation() + " + FeatureB"; + } + }; + + class ConcreteDecoratorC : public BaseDecorator + { + public: + explicit ConcreteDecoratorC(IComponent *component) : BaseDecorator{component} {} + + std::string operation() const override + { + return BaseDecorator::operation() + " + FeatureC"; + } + }; + + namespace Client + { + void clientCode(const IComponent &comp) + { + std::cout << comp.operation() << "\n"; + } + } + + void run() + { + std::cout << "\n\nDecorator\n"; + IComponent *simple = new ConcreteComponent; + Client::clientCode(*simple); + + IComponent *withA = new ConcreteDecoratorA(simple); + Client::clientCode(*withA); + + IComponent *withB = new ConcreteDecoratorB(simple); + Client::clientCode(*withB); + + IComponent *withAB = new ConcreteDecoratorB(withA); + Client::clientCode(*withAB); + + IComponent *withABC = new ConcreteDecoratorC(withAB); + Client::clientCode(*withABC); + + delete simple; + delete withA; + delete withB; + delete withAB; + delete withABC; + } + } + +} + +struct DecoratorAutoRuner +{ + DecoratorAutoRuner() + { + std::cout << "\n--- Decorator Pattern Example ---\n"; + Problem::run(); + DecoratorPattern::run(); + } +}; + +static DecoratorAutoRuner instance; \ No newline at end of file diff --git a/src/patterns/structural/Facade.cpp b/src/patterns/structural/Facade.cpp index d5a0742..6f708e2 100644 --- a/src/patterns/structural/Facade.cpp +++ b/src/patterns/structural/Facade.cpp @@ -5,7 +5,7 @@ // Appicability: // (*) when you need to have a limited but straightforward interface to a complex subsystem. // (**) when you want to structure a subsystem into layers. -// UML: none +// UML: docs/uml/patterns_structural_facade.drawio.svg #include namespace
<<Interface>>IComponent
+ operation(Type): Type