//+------------------------------------------------------------------+ //| Visitor.mq5 | //| 2019-2020, dimitri pecheritsa | //| 792112@gmail.com | //+------------------------------------------------------------------+ // // visitor - behavioral design pattern // // from: design patterns: elements of reusable object-oriented software // by gof: erich gamma, richard helm, ralph johnson, john vlissides // published in 1994 // // intent // // represent an operation to be performed on the elements of an object //structure. visitor lets you define a new operation without changing //the classes of the elements on which it operates. // // applicability // // use the visitor pattern when // an object structure contains many classes of objects with differing //interfaces, and you want to perform operations on these objects that //depend on their concrete classes. // many distinct and unrelated operations need to be performed on //objects in an object structure, and you want to avoid "polluting" their //classes with these operations. visitor lets you keep related operations //together by defining them in one class. when the object structure is //shared by many applications, use visitor to put operations in just those //applications that need them. // the classes defining the object structure rarely change, but you //often want to define new operations over the structure. changing the //object structure classes requires redefining the interface to all visitors, //which is potentially costly. if the object structure classes change //often, then it's probably better to define the operations in those classes. // // structure // //|Client|---------->| Visitor | // | |---------------------------------------| // | |VisitConcreteElementA(ConcreteElementA)| // | |VisitConcreteElementB(ConcreteElementB)| // | ^ // | | // | +---------------+---------------+ // | | | // | | ConcreteVisitor1 | | ConcreteVisitor2 | // | |---------------------- | |-----------------------| // | |VisitConcreteElementA( | |VisitConcreteElementA( | // | | ConcreteElementA) | | ConcreteElementA) | // | |VisitConcreteElementB( | |VisitConcreteElementB( | // | | ConcreteElementB) | | ConcreteElementB) | // | // | // +--->|ObjectStructure|--->*| Element | // |---------------| // |Accept(Visitor)| // ^ // | // +------------------+------------------+ // | | // | ConcreteElementA | | ConcreteElementB | // |------------------------------| |------------------------------| // |Accept(Visitor) | |Accept(Visitor) | // | v.VisitConcreteElementA(this)| | v.VisitConcreteElementB(this)| // |OperationA() | |OperationB() | // // // participants // // visitor // declares a visit operation for each class of concrete element //in the object structure. the operation's name and signature identifies //the class that sends the visit request to the visitor. that lets the //visitor determine the concrete class of the element being visited. then //the visitor can access the element directly through its particular interface. // concrete visitor // implements each operation declared by visitor. each operation //implements a fragment of the algorithm defined for the corresponding //class of object in the structure. concrete visitor provides the context //for the algorithm and stores its local state. this state often accumulates //results during the traversal of the structure. // element // defines an accept operation that takes a visitor as an argument. // concrete element // implements an accept operation that takes a visitor as an argument. // object structure // can enumerate its elements. // may provide a high-level interface to allow the visitor to visit //its elements. // may either be a composite or a collection such as a list or a set. // // collaborations // // a client that uses the visitor pattern must create a concrete visitor //object and then traverse the object structure, visiting each element //with the visitor. // when an element is visited, it calls the visitor operation that corresponds //to its class. the element supplies itself as an argument to this operation //to let the visitor access its state, if necessary. // the following interaction diagram illustrates the collaborations //between an object structure, a visitor, and two elements: // // anObject aConcreteElementA aConcreteElementB aConcreteVisitor // Structure | | | // | | | | // | |Accept(aVisitor) | | | // | |---------------->| |VisitConcreteElementA(aConcreteElementA) | // | | | |------------------|-------------------->| | // | | | | | | // | | | |<-----------------|---------------------| | // | |Accept(aVisitor) | | | // | |------------------|----------------->| |VisitConcrete | // | | | | | ElementB(aConcrete | | // | | | | | ElementB) | | // | | | | |------------------->| | // | | | | | | // | | | | |<-------------------| | // | | | | // //+------------------------------------------------------------------+ //| example of a client | //+------------------------------------------------------------------+ #include <MqhPatternsVisitorObjectStructure.mqh> #include <MqhPatternsVisitorElement.mqh> #include <MqhPatternsVisitorConcreteElementA.mqh> #include <MqhPatternsVisitorConcreteElementB.mqh> #include <MqhPatternsVisitorVisitor.mqh> #include <MqhPatternsVisitorConcreteVisitor1.mqh> #include <MqhPatternsVisitorConcreteVisitor2.mqh> void OnStart() { ObjectStructure structure; structure.Add(new ConcreteElementA()); structure.Add(new ConcreteElementB()); structure.Accept(new ConcreteVisitor1()); structure.Accept(new ConcreteVisitor2()); } // output // // element 2097152 added to the object structure // element 3145728 added to the object structure // // object structure has accepted new visitor 4194304 // object structure has 2 elements // object structure is requesting each element to accept the visitor // element a has accepted the visitor // the element made operation a // element b has accepted the visitor // the element made operation b // // object structure has accepted new visitor 5242880 // object structure has 2 elements // object structure is requesting each element to accept the visitor // element a has accepted the visitor // the element made operation a // element b has accepted the visitor // the element made operation b // // // consequences // // some of the benefits and liabilities of the visitor pattern are as //follows: // visitor makes adding new operations easy // a visitor gathers related operations and separates unrelated ones // adding new concrete element classes is hard // visiting across class hierarchies // accumulating state // breaking encapsulation // // implementation // // each object structure will have an associated visitor class. this //abstract visitor class declares a visit concrete element operation for //each class of concrete element defining the object structure. each visit //operation on the visitor declares its argument to be a particular concrete //element, allowing the visitor to access the interface of the concrete //element directly. concrete visitor classes override each visit operation //to implement visitor-specific behavior for the corresponding concrete //element class. // here are two other implementation issues that arise when you apply //the visitor pattern: // double dispatch // who is responsible for traversing the object structure? // // related patterns // // composite: visitors can be used to apply an operation over an object //structure defined by the composite pattern. // interpreter: visitor may be applied to do the interpretation. // //+------------------------------------------------------------------+