//+------------------------------------------------------------------+ //| ChainOfResponsibility.mqh | //| 2019-2020, dimitri pecheritsa | //| 792112@gmail.com | //+------------------------------------------------------------------+ //| chain of responsibility — behavioral design pattern | //+------------------------------------------------------------------+ // design patterns: elements of reusable object-oriented software // gof > erich gamma, richard helm, ralph johnson, john vlissides // published — 1994 //+------------------------------------------------------------------+ //| intent | //+------------------------------------------------------------------+ // avoid coupling the sender of a request to its receiver // by giving more than one object a chance to handle the request // chain the receiving objects and pass the request along the chain // until an object handles it //+------------------------------------------------------------------+ //| benefits | //+------------------------------------------------------------------+ // aspect that may vary > object that can fulfill a request // code refactoring // problems // dependence on specific operations // tight coupling // extending functionality by subclassing // solutions // avoid — hard-coded requests // also > command // decouple — with abstract coupling and layering // also > abstract factory, bridge, command, facade, // mediator, observer // use composition/delegation — for combining behavior // also > bridge, composite, decorator, observer, strategy //+------------------------------------------------------------------+ //| applicability | //+------------------------------------------------------------------+ // more than one object may handle a request, // and the handler isn't known a priori // the handler should be ascertained automatically // you want to issue a request to one of several objects // without specifying the receiver explicitly // the set of objects that can handle a request // should be specified dynamically //+------------------------------------------------------------------+ //| structure | //+------------------------------------------------------------------+ // // +----------------+ // | | // v successor | // |Client|------------>| Handler |-------------+ // |---------------| // |HandleRequest()| // ^ // | // +------------+-------------+ // | | // |ConcreteHandler1| |ConcreteHandler2| // |----------------| |----------------| // |HandleRequest() | |HandleRequest() | // //+------------------------------------------------------------------+ //| a typical object structure | //+------------------------------------------------------------------+ // // |aClient | // |-----------| |aConcreteHandler| // |aHandler *-|----->|----------------| |aConcreteHandler| // |successor *-|------>|----------------| // |successor | // #include <SRCPatternsPatternOrganizer.mqh> namespace ChainOfResponsibility { //+------------------------------------------------------------------+ //| participants > handler | //+------------------------------------------------------------------+ class Handler // defines an interface for handling requests // (optional) implements the successor link { public: Handler* successor; virtual void HandleRequest(int)=0; ~Handler(void); }; //+------------------------------------------------------------------+ //| participants > handler > destructor | //+------------------------------------------------------------------+ Handler::~Handler(void) { delete successor; } //+------------------------------------------------------------------+ //| participants > concrete handler | //+------------------------------------------------------------------+ // handles requests it is responsible for // can access its successor // if the concretehandler can handle the request, it does so // else it forwards the request to its successor //+------------------------------------------------------------------+ //| participants > concrete handler > 1 | //+------------------------------------------------------------------+ class ConcreteHandler1:public Handler { public: void HandleRequest(int); }; //+------------------------------------------------------------------+ //| participants > concrete handler > 1 > handle request | //+------------------------------------------------------------------+ void ConcreteHandler1::HandleRequest(int request) { if(request==1) Print("request: ",request,". response: one. handled by: ",&this); else if(CheckPointer(successor)) { Print("request: ",request,". cannot be handled by ",&this,", forwarding to successor..."); successor.HandleRequest(request); } } //+------------------------------------------------------------------+ //| participants > concrete handler > 2 | //+------------------------------------------------------------------+ class ConcreteHandler2:public Handler { public: void HandleRequest(int); }; //+------------------------------------------------------------------+ //| participants > concrete handler > 2 > handle request | //+------------------------------------------------------------------+ void ConcreteHandler2::HandleRequest(int request) { if(request==2) Print("request: ",request,". response: two. handled by: ",&this); else if(CheckPointer(successor)) { Print("request: ",request,". cannot be handled by ",&this,", forwarding to successor..."); successor.HandleRequest(request); } } //+------------------------------------------------------------------+ //| participants > client | //+------------------------------------------------------------------+ class Client:public ClientExample // initiates the request to a concrete handler on the chain { public: string Output(); void Run(); }; string Client::Output() { return __FUNCTION__; } //+------------------------------------------------------------------+ //| collaborations | //+------------------------------------------------------------------+ void Client::Run() // request is sent along the chain until concrete handler handles it { Handler* h1=new ConcreteHandler1(); Handler* h2=new ConcreteHandler2(); h1.successor=h2; h1.HandleRequest(1); // will handle the request h1.HandleRequest(2); // will pass the request to successor //--- delete h1; } } //+------------------------------------------------------------------+ //| output | //+------------------------------------------------------------------+ // ChainOfResponsibility::Client::Output // request: 1. response: one. handled by: 2097152 // request: 2. cannot be handled by 2097152, forwarding to successor... // request: 2. response: two. handled by: 3145728 //+------------------------------------------------------------------+ //| consequences | //+------------------------------------------------------------------+ // reduced coupling // object does not know who handles a request // object knows that a request will be handled // sender does not know receiver // object does not know the chain structure // simple object interconnections // objects do not maintain references to all candidate receivers // objects keep a reference to successor only // flexibility in assigning responsibilities to objects // add/change the chain at run-time // combine with subclassing to specialize handlers statically // receipt isn't guaranteed // request has no explicit receiver // request can fall off the end of the chain // also when the chain is not configured properly //+------------------------------------------------------------------+ //| implementation | //+------------------------------------------------------------------+ // successor chain // define new links // use existing links when the links support the chain // saves from defining links explicitly // saves space // if other structure then define redundant links // connecting successors // if no preexisting references for defining a chain // then handler defines the interface, maintains the successor // handler provides a default implementation of handlerequest // concrete handler doesn't have to override the forwarding operation // representing requests // request is a hard-coded operation call // convenient and safe // can forward only handler's fixed set of requests // or use a single handler function with request code as a parameter // supports an open-ended requests // sender/receiver must agree on encoding // requires conditional statements for dispatching the request // less safe than direct call // can use separate requests that bundle parameters // request can represent requests explicitly // can subclass new requests // subclasses can define different parameters // handlers must know the kind of request // identify the request by accessor function — returns class id // or receiver can use run-time type information //+------------------------------------------------------------------+ //| related patterns | //+------------------------------------------------------------------+ // composite > component's parent can act as its successor //+------------------------------------------------------------------+