//+------------------------------------------------------------------+ //| Decorator.mqh | //| 2019-2020, dimitri pecheritsa | //| 792112@gmail.com | //+------------------------------------------------------------------+ //| decorator < structural design pattern | //+------------------------------------------------------------------+ // design patterns: elements of reusable object-oriented software // gof > erich gamma, richard helm, ralph johnson, john vlissides // published in 1994 //+------------------------------------------------------------------+ //| intent | //+------------------------------------------------------------------+ // attach — additional responsibilities — to an object dynamically // provide — flexible alternative to subclassing — for extending // functionality //+------------------------------------------------------------------+ //| benefits | //+------------------------------------------------------------------+ // variable aspect > responsibilities of an object without subclassing // code refactoring // inability to alter classes conveniently > also > adapter, visitor // extending functionality by subclassing // solution > composition/delegation for combining behavior // also > bridge, chain of responsibility, composite, // observer, strategy //+------------------------------------------------------------------+ //| applicability | //+------------------------------------------------------------------+ // add responsibilities — to individual objects — // dynamically and transparently — without affecting other objects // for responsibilities that can be withdrawn // when extension by subclassing is impractical // sometimes a large number of independent extensions are possible // and would produce an explosion of subclasses to support // every combination // or a class definition may be hidden // or otherwise unavailable for subclassing //+------------------------------------------------------------------+ //| structure | //+------------------------------------------------------------------+ // // | Component |<------------------------------+ // |-----------| | // |Operation()| | // ^ | // | | // +-----------+-------------+ | // | | component | // |ConcreteComponent| | Decorator |-----------+ // |-----------------| |-----------------------| // |Operation() | |Operation() | // | component.Operation()| // ^ // | // +-------------+----------+ // | | // |ConcreteDecoratorA| | ConcreteDecoratorB | // |------------------| |-----------------------| // |Operation() | |Operation() | // |------------------| | Decorator::Operation()| // |added_state | | AddedBehavior() | // |AddedBehavior() | // #include <SRCPatternsPatternOrganizer.mqh> namespace Decorator { //+------------------------------------------------------------------+ //| participants > component | //+------------------------------------------------------------------+ class Component // defines — interface for objects // can have responsibilities added to them dynamically { public: virtual void Operation(void)=0; }; //+------------------------------------------------------------------+ //| participants > decorator | //+------------------------------------------------------------------+ class Decorator:public Component // maintains — reference to a component object // defines — interface — conforms to component's interface { public: Component* component; void Operation(void); }; //+------------------------------------------------------------------+ //| participants > decorator | //+------------------------------------------------------------------+ void Decorator::Operation(void) { if(CheckPointer(component)>0) { component.Operation(); } } //+------------------------------------------------------------------+ //| participants > concrete component | //+------------------------------------------------------------------+ class ConcreteComponent:public Component // defines — object — to which additional responsibilities can be attached { public: void Operation(void); }; //+------------------------------------------------------------------+ //| participants > concrete component | //+------------------------------------------------------------------+ void ConcreteComponent::Operation(void) { Print("concrete operation"); } //+------------------------------------------------------------------+ //| participants > concrete decorator | //+------------------------------------------------------------------+ // adds responsibilities to the component //+------------------------------------------------------------------+ //| participants > concrete decorator > a | //+------------------------------------------------------------------+ class ConcreteDecoratorA:public Decorator { protected: string added_state; public: ConcreteDecoratorA(void); void Operation(void); }; //+------------------------------------------------------------------+ //| participants > concrete decorator > a | //+------------------------------------------------------------------+ ConcreteDecoratorA::ConcreteDecoratorA(void): added_state("added state") { } //+------------------------------------------------------------------+ //| participants > concrete decorator > a | //+------------------------------------------------------------------+ void ConcreteDecoratorA::Operation(void) { Decorator::Operation(); Print(added_state); } //+------------------------------------------------------------------+ //| participants > concrete decorator > b | //+------------------------------------------------------------------+ class ConcreteDecoratorB:public Decorator { public: void AddedBehavior(void); void Operation(void); }; //+------------------------------------------------------------------+ //| participants > concrete decorator > b | //+------------------------------------------------------------------+ void ConcreteDecoratorB::AddedBehavior(void) { Print("added behavior"); } //+------------------------------------------------------------------+ //| participants > concrete decorator > b | //+------------------------------------------------------------------+ void ConcreteDecoratorB::Operation(void) { Decorator::Operation(); AddedBehavior(); } //+------------------------------------------------------------------+ //| participants > client | //+------------------------------------------------------------------+ class Client:public ClientExample { public: string Output(void); void Run(void); }; string Client::Output(void) {return __FUNCTION__;} //+------------------------------------------------------------------+ //| collaborations | //+------------------------------------------------------------------+ void Client::Run(void) // decorator // forwards — requests — to its component object // it may optionally perform — additional operations before and // after forwarding the request { Component* component=new ConcreteComponent(); Decorator* decorator_a=new ConcreteDecoratorA(); Decorator* decorator_b=new ConcreteDecoratorB(); decorator_a.component=component; decorator_b.component=decorator_a; decorator_b.Operation(); //--- delete component; delete decorator_a; delete decorator_b; } } //+------------------------------------------------------------------+ //| output | //+------------------------------------------------------------------+ // Decorator::Client::Output // concrete operation // added state // added behavior //+------------------------------------------------------------------+ //| consequences | //+------------------------------------------------------------------+ // more flexibility than static inheritance // responsibilities can be added and removed at run-time // simply by attaching and detaching them // easy to add a property twice // avoids feature-laden classes high up in the hierarchy // add functionality incrementally // define — new decorators independently // from the classes of objects they extend // even for unforeseen extensions // a decorator and its component aren't identical // lots of little objects //+------------------------------------------------------------------+ //| implementation | //+------------------------------------------------------------------+ // interface conformance > decorator-component // omitting the abstract decorator class — if single responsibility // keeping components lightweight // changing the skin of an object versus changing its guts > strategy //+------------------------------------------------------------------+ //| related patterns | //+------------------------------------------------------------------+ // adapter > will give an object a completely — new interface // decorator > changes object's responsibilities, not its interface // composite // decorator — is a degenerate composite with only one component — // not intended for object aggregation // strategy > change the guts of an object // decorator > change the skin of an object //+------------------------------------------------------------------+