//+------------------------------------------------------------------+ //| Composite.mqh | //| 2019-2020, dimitri pecheritsa | //| 792112@gmail.com | //+------------------------------------------------------------------+ //| src > patterns > structural > composite | //+------------------------------------------------------------------+ // design patterns: elements of reusable object-oriented software // gof > erich gamma, richard helm, ralph johnson, john vlissides // published in 1994 //+------------------------------------------------------------------+ //| intent | //+------------------------------------------------------------------+ // object tree > represents > part-whole hierarchies // client > treats uniformly > objects and compositions of objects //+------------------------------------------------------------------+ //| benefits | //+------------------------------------------------------------------+ // variable aspect > structure and composition of an object // refactoring > problem > extending functionality < by subclassing // refactoring > solution > composition/delegation > combine behavior // also > bridge, chain of responsibility, composite, decorator, // observer, strategy //+------------------------------------------------------------------+ //| applicability | //+------------------------------------------------------------------+ // represent > part-whole hierarchies of objects // clients > ignore > difference // compositions of objects and individual objects // clients > treat uniformly > composite structure objects //+------------------------------------------------------------------+ //| structure | //+------------------------------------------------------------------+ // // |Client|----->| Component |*<------------------+ // |-----------------| | // |Operation() | | // |Add(Component) | | // |Remove(Component)| | // |GetChild(int) | | // ^ | // | | // +-------+-----------+ | // | | nodes | // | Leaf | | Composite |o------+ // |-----------| |-------------------| // |Operation()| |Operation() | // | for all n in nodes| // | n.Operation() | // |Add(Component) | // |Remove(Component) | // |GetChild(int) | // //+------------------------------------------------------------------+ //| typical object structure | //+------------------------------------------------------------------+ // // +---->|aLeaf| // | // |aComposite|-----+---->|aLeaf| +---->|aLeaf| // | | // +---->|aComposite|----+---->|aLeaf| // | | // +---->|aLeaf| +---->|aLeaf| // #include <SRCPatternsPatternOrganizer.mqh> namespace Composite { //+------------------------------------------------------------------+ //| participants | //+------------------------------------------------------------------+ class Component // declares > interface // objects < composition // accessing and managing child components // default behavior > interface > common to all classes // optional > interface // accessing > component's parent in the recursive structure // implements it if that's appropriate { public: virtual void Operation(void)=0; virtual void Add(Component*)=0; virtual void Remove(Component*)=0; virtual Component* GetChild(int)=0; Component(void); Component(string); protected: string name; }; Component::Component(void) {} Component::Component(string a_name):name(a_name) {} //+------------------------------------------------------------------+ //| participants | //+------------------------------------------------------------------+ #define ERR_INVALID_OPERATION_EXCEPTION 1 //user error for adding/removing a component to a leaf class Leaf:public Component // represents > leaf objects < composition // no children // defines > behavior > primitive objects in the composition { public: void Operation(void); void Add(Component*); void Remove(Component*); Component* GetChild(int); Leaf(string); }; void Leaf::Leaf(string a_name):Component(a_name) {} void Leaf::Operation(void) {Print(name);} void Leaf::Add(Component*) {SetUserError(ERR_INVALID_OPERATION_EXCEPTION);} void Leaf::Remove(Component*) {SetUserError(ERR_INVALID_OPERATION_EXCEPTION);} Component* Leaf::GetChild(int) {SetUserError(ERR_INVALID_OPERATION_EXCEPTION); return NULL;} //+------------------------------------------------------------------+ //| participants | //+------------------------------------------------------------------+ class Composite:public Component // defines > behavior for components having children // stores > child components // implements > child-related operations > in component interface { public: void Operation(void); void Add(Component*); void Remove(Component*); Component* GetChild(int); Composite(string); ~Composite(void); protected: Component* nodes[]; }; Composite::Composite(string a_name):Component(a_name) {} //+------------------------------------------------------------------+ //| participants > composite | //+------------------------------------------------------------------+ Composite::~Composite(void) { int total=ArraySize(nodes); for(int i=0; i<total; i++) { Component* i_node=nodes[i]; if(CheckPointer(i_node)==1) { delete i_node; } } } //+------------------------------------------------------------------+ //| participants > composite | //+------------------------------------------------------------------+ void Composite::Operation(void) { Print(name); int total=ArraySize(nodes); for(int i=0; i<total; i++) { nodes[i].Operation(); } } //+------------------------------------------------------------------+ //| participants > composite | //+------------------------------------------------------------------+ void Composite::Add(Component *src) { int size=ArraySize(nodes); ArrayResize(nodes,size+1); nodes[size]=src; } //+------------------------------------------------------------------+ //| participants > composite | //+------------------------------------------------------------------+ void Composite::Remove(Component *src) { int find=-1; int total=ArraySize(nodes); for(int i=0; i<total; i++) { if(nodes[i]==src) { find=i; break; } } if(find>-1) { ArrayRemove(nodes,find,1); } } //+------------------------------------------------------------------+ //| participants > composite | //+------------------------------------------------------------------+ Component* Composite::GetChild(int i) { return nodes[i]; } //+------------------------------------------------------------------+ //| participants | //+------------------------------------------------------------------+ class Client:public ClientExample // manipulates objects in the composition through the component interface { public: string Output(void); void Run(void); }; string Client::Output(void) {return __FUNCTION__;} //+------------------------------------------------------------------+ //| collaborations | //+------------------------------------------------------------------+ void Client::Run(void) // clients use the component class interface // to interact with objects in the composite structure // if the recipient is a leaf > the request is handled directly // if the recipient is a composite // it usually forwards requests to its child components // possibly performing additional operations // before and/or after forwarding { Component* root=new Composite("root"); //make root //---make components Component* branch1=new Composite(" branch 1"); Component* branch2=new Composite(" branch 2"); Component* leaf1=new Leaf(" leaf 1"); Component* leaf2=new Leaf(" leaf 2"); //---build tree root.Add(branch1); root.Add(branch2); branch1.Add(leaf1); branch1.Add(leaf2); branch2.Add(leaf2); branch2.Add(new Leaf(" leaf 3")); //---check printf("tree:"); root.Operation(); //---change tree root.Remove(branch1); //remove whole branch //---check printf("tree after removal of one branch:"); root.Operation(); //---finish delete root; delete branch1; } } //+------------------------------------------------------------------+ //| output | //+------------------------------------------------------------------+ // Structural::Composite::Client::Output // tree: // root // branch 1 // leaf 1 // leaf 2 // branch 2 // leaf 2 // leaf 3 // tree after removal of one branch: // root // branch 2 // leaf 2 // leaf 3 //+------------------------------------------------------------------+ //| consequences | //+------------------------------------------------------------------+ // defines class hierarchies consisting of primitive objects and composite objects // makes the client simple // makes it easier to add new kinds of components // can make your design overly general //+------------------------------------------------------------------+ //| implementation | //+------------------------------------------------------------------+ // explicit parent references (in component) // simplify the traversal and management of a composite structure // help support the chain of responsibility pattern // sharing components // flyweight can rework a design to avoid storing parents altogether // maximizing the component interface // default implementations, leaf and composite will override them // declaring the child management operations // at the root of the class hierarchy > transparency // in the composite > afety // usually it's better to make add and remove fail by default // if the component isn't allowed to have children // or if the argument of remove isn't a child of the component // component may implement a list of components // if there are relatively few children in the structure // child ordering > design child access/management carefully > iterator // caching to improve performance // when components know their parents // tell composites that their caches are invalid // composite is responsible for deleting its children // when it's destroyed, unless leafs can be shared // composites store children in // linked lists, trees, arrays, and hash tables //+------------------------------------------------------------------+ //| related patterns | //+------------------------------------------------------------------+ // chain of responsibility > component-parent link // decorator > common parent class > for decorators and composites // flyweight > share components (no longer refer to their parents) // iterator > traverse composites // visitor > localizes operations and behavior // distributed across composite and leaf classes //+------------------------------------------------------------------+