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): Type

ConcreteComponent
ConcreteComponentWithA
ConcreteComponentWithB
ConcreteComponentWithAB
ConcreteComponentWithC
ConcreteComponentWithA1
Use
Extends
Extends
Extends
Extends
Extends
Client
+ clientCode(IComponent* comp): type

<<Interface>>
IComponent


+ operation(Type): Type

Use
BaseDecorator
+ m_component: *IComponent
+ BaseDecorator(IComponent* comp)

+ operation(Type): Type
ConcreteDecoratorA
+ field: type
ConcreteDecoratorA(IComponent* comp)
+ method(type): type
ConcreteDecoratorB
+ field: type
ConcreteDecoratorB(IComponent* comp)
+ method(type): type
ConcreteDecoratorC
+ field: type
ConcreteDecoratorB(IComponent* comp)
+ method(type): type
ConcreteComponent
+ operation(Type): Type
1
Extends
Extends
Extends
method:
{
 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