// Purpose. Visitor (adding ops) #include // class Visitor; // Discussion. On the left, adding // new operations requires modifying class Color { public: // all the current Color classes. If virtual void accept( Visitor& ) = 0; // we introduce an extra level of in- }; // direction (a Visitor hierarchy), // then operations capable of opera- class Red : public Color { public: // ting on Color classes can be added void accept( Visitor& ); // without modifying any existing void eye() { cout << "Red::eye\n"; } // code. On the right, an entry point }; // for all future operations has been class Blu : public Color { public: // added in the form of the accept() void accept( Visitor& ); // method. Each of the previous op- void sky() { cout << "Blu::sky\n"; } // erations has been encapsulated in }; // its own derived Visitor class. Any // new operations simply require the class Visitor { public: // addition of a new Visitor class. virtual void visit( Red& ) = 0; // virtual void visit( Blu& ) = 0; // When we call accept() on a Color }; // object, dynamic binding gets us to // the correct derived class of Color. class CountV : public Visitor { // Then when we call visit() on the public: // Visitor object, dynamic binding CountV() { numRed_ = numBlu_ = 0; } // gets us to the correct derived virtual void visit( Red& ) { // class of Visitor. [Visitors and numRed_++; } // Colors can be passed by address or virtual void visit( Blu& ) { // passed by reference.] numBlu_++; } void reportNum() { #include cout << "Reds " << numRed_ << ", Blus " << numBlu_ << endl; } class Color { private: public: int numRed_, numBlu_; virtual void count() = 0; }; virtual void call() = 0; static void reportNum() { class CallV : public Visitor { public: cout << "Reds " << numRed_ << virtual void visit( Red& r ) { ", Blus " << numBlu_ <accept( countOp ); set[i]->accept( callOp ); } void main( void ) countOp.reportNum(); { } Color* set[] = { new Red, new Blu, new Blu, new Red, new Red, 0 }; // Red::eye for (int i=0; set[i]; i++) { // Blu::sky set[i]->count(); // Blu::sky set[i]->call(); } // Red::eye Color::reportNum(); // Red::eye } // Reds 3, Blus 2 // Purpose. Visitor (double dispatch) // // Discussion. On the left, the State derived classes must query the type of // the Cmd objects they receive, in order to identify what the next course of // action is. "case" stmts are always a maintenance headache. On the right, // we have recognized that what we really want to do is "dispatch" based on // the type of TWO objects, (a State object and a Cmd object). The call to // accept() discriminates the type of the State object that is being messaged, // and then the call to visit() discriminates the type of the Cmd object // (while passing the type of the State object). If new Cmd classes are // added, no change whatsoever is necessary in the code of the State classes. // If new State classes are added, then every Cmd class must be changed, and // Visitor is NOT the right approach to take. #include #include int current = 0; int current = 0; enum CmdTyp { OnT, OffT }; class One; class Two; class Cmd { public: class Cmd { public: virtual CmdTyp typ() = 0; virtual void visit( One* ) { }; cout << "ERROR\n"; } class On : public Cmd { public: virtual void visit( Two* ) { CmdTyp typ() { return OnT; } cout << "ERROR\n"; } }; }; class Off : public Cmd { public: class On : public Cmd { public: CmdTyp typ() { return OffT; } void visit( One* ) { }; current = 1; cout << "One,On => Two\n"; } class State { public: void visit( Two* t ) { virtual void process( Cmd* c ) { Cmd::visit( t ); } cout << "ERROR\n"; } }; }; class Off : public Cmd { public: class One : public State { public: void visit( One* o ) { void process( Cmd* c ) { Cmd::visit( o ); } if (c->typ() == OnT) { void visit( Two* ) { current = 1; current = 0; cout << "One,On => Two\n"; } cout << "Two,Off => One\n"; } else if (c->typ() == OffT) }; State::process( c ); } class State { public: }; virtual void accept( Cmd* c ) = 0; class Two : public State { public: }; void process( Cmd* c ) { class One : public State { public: if (c->typ() == OnT) void accept( Cmd* c ) { State::process( c ); c->visit( this ); } else if (c->typ() == OffT) { }; current = 0; class Two : public State { public: cout << "Two,Off => One\n"; } void accept( Cmd* c ) { } c->visit( this ); } }; }; State* states[] = { new One, new Two }; State* states[] = { new One, new Two }; void main( void ) void main( void ) { { Cmd* c[] = { new Off, Cmd* c[] = { new Off, new On, new Off, new Off, 0 }; new On, new Off, new Off, 0 }; for (int i=0; c[i]; i++) for (int i=0; c[i]; i++) states[current]->process( c[i] ); states[current]->accept( c[i] ); } } // ERROR // ERROR // One,On => Two // One,On => Two // Two,Off => One // Two,Off => One // ERROR // ERROR // Purpose. Visitor design pattern // 1. Add an accept(Visitor) method to the "element" hierarchy // 2. Create a "visitor" base class w/ a visit() method for every "element" type // 3. Create a "visitor" derived class for each "operation" to do on "elements" // 4. Client creates "visitor" objects and passes each to accept() calls #include #include using namespace std; // 1. Add an accept(Visitor) method to the "element" hierarchy class Element { public: virtual void accept( class Visitor& v ) = 0; }; class This : public Element { public: /*virtual*/ void accept( Visitor& v ); string thiss() { return "This"; } }; class That : public Element { public: /*virtual*/ void accept( Visitor& v ); string that() { return "That"; } }; class TheOther : public Element { public: /*virtual*/ void accept( Visitor& v ); string theOther() { return "TheOther"; } }; // 2. Create a "visitor" base class w/ a visit() method for every "element" type class Visitor { public: virtual void visit( This* e ) = 0; virtual void visit( That* e ) = 0; virtual void visit( TheOther* e ) = 0; }; /*virtual*/ void This::accept( Visitor& v ) { v.visit( this ); } /*virtual*/ void That::accept( Visitor& v ) { v.visit( this ); } /*virtual*/ void TheOther::accept( Visitor& v ) { v.visit( this ); } // 3. Create a "visitor" derived class for each "operation" to do on "elements" class UpVisitor : public Visitor { /*virtual*/ void visit( This* e ) { cout << "do Up on " + e->thiss() << '\n'; } /*virtual*/ void visit( That* e ) { cout << "do Up on " + e->that() << '\n'; } /*virtual*/ void visit( TheOther* e ) { cout << "do Up on " + e->theOther() << '\n'; } }; class DownVisitor : public Visitor { /*virtual*/ void visit( This* e ) { cout << "do Down on " + e->thiss() << '\n'; } /*virtual*/ void visit( That* e ) { cout << "do Down on " + e->that() << '\n'; } /*virtual*/ void visit( TheOther* e ) { cout << "do Down on " + e->theOther() << '\n'; } }; void main( void ) { Element* list[] = { new This(), new That(), new TheOther() }; UpVisitor up; // 4. Client creates DownVisitor down; // "visitor" objects for (int i=0; i < 3; i++) // and passes each list[i]->accept( up ); // to accept() calls for (i=0; i < 3; i++) list[i]->accept( down ); } // do Up on This // do Down on This // do Up on That // do Down on That // do Up on TheOther // do Down on TheOther // Purpose. Visitor - recovering lost type information // // Motivation. "My Component classes do not know that Composites exist. // They provide no help for navigating Composites, nor any help for // altering the contents of a Composite. This is because I would like the // base class (and all its derivatives) to be reusable in contexts that do // not require Composites. When given a base class pointer, if I // absolutely need to know whether or not it is a Composite, I will use // dynamic_cast() to figure this out. In those cases where dynamic_cast() // is too expensive, I will use a Visitor." [Robert Martin] #include #include using namespace std; class Visitor { public: virtual void visit( class Primitive*, class Component* ) = 0; virtual void visit( class Composite*, Component* ) = 0; }; class Component { int value; public: Component( int val ) { value = val; } virtual void traverse() { cout << value << " "; } // Having add() here sacrifices safety, but it supports transparency // virtual void add( Component* ) { } virtual void accept( Visitor&, Component* ) = 0; }; class Primitive : public Component { public: Primitive( int val ) : Component( val ) { } /*virtual*/ void accept( Visitor& v, Component* c ) { v.visit( this, c ); } }; class Composite : public Component { vector children; public: Composite( int val ) : Component( val ) { } void add( Component* ele ) { children.push_back( ele ); } /*virtual*/ void accept( Visitor& v, Component* c ) { v.visit( this, c ); } /*virtual*/ void traverse() { Component::traverse(); for (int i=0; i < children.size(); i++) children[i]->traverse(); } }; class AddVisitor : public Visitor { public: /*virtual*/ void visit( Primitive*, Component* ) {/* does not make sense */} /*virtual*/ void visit( Composite* node, Component* c ) { node->add( c ); } }; void main( void ) { Component* nodes[3]; // The type of Composite* is "lost" when the object is assigned to a // Component* nodes[0] = new Composite(1); nodes[1] = new Composite(2); nodes[2] = new Composite(3); // If add() were in class Component, this would work // nodes[0]->add( nodes[1] ); // If it is NOT in Component, and only in Composite, you get the error - // no member function `Component::add(Component *)' defined // Instead of sacrificing safety, we use a Visitor to support add() AddVisitor addVisitor; nodes[0]->accept( addVisitor, nodes[1] ); nodes[0]->accept( addVisitor, nodes[2] ); nodes[0]->accept( addVisitor, new Primitive(4) ); nodes[1]->accept( addVisitor, new Primitive(5) ); nodes[1]->accept( addVisitor, new Primitive(6) ); nodes[2]->accept( addVisitor, new Primitive(7) ); for (int i=0; i < 3; i++) { nodes[i]->traverse(); cout << endl; } } // 1 2 5 6 3 7 4 // 2 5 6 // 3 7 // Purpose. Combining Visitor with Composite's recursive traversal #include #include using namespace std; class Visitor { public: virtual void visit( class Leaf* e ) = 0; virtual void visit( class Composite* e ) = 0; }; class Component { public: virtual void traverse() = 0; virtual void accept( class Visitor& v ) = 0; }; class Leaf : public Component { int value; public: Leaf( int val ) { value = val; } /*virtual*/ void traverse() { cout << value << ' '; } /*virtual*/ void accept( class Visitor& v ) { v.visit( this ); } int getValue() { return value; } }; class Composite : public Component { char value; vector children; static char next; public: Composite() { value = next++; } void add( Component* ele ) { children.push_back( ele ); } /*virtual*/ void traverse() { cout << value << ' '; for (int i=0; i < children.size(); i++) children[i]->traverse(); } /*virtual*/ void accept( class Visitor& v ) { v.visit( this ); // accept() has been embellished to include the logic in traverse() for (int i=0; i < children.size(); i++) children[i]->accept( v ); } char getValue() { return value; } }; char Composite::next = 'a'; class TransformVisitor : public Visitor { public: /*virtual*/ void visit( Leaf* e ) { cout << e->getValue() + 100 << ' '; } /*virtual*/ void visit( Composite* e ) { cout << (char)(e->getValue()-32) <<' ';} }; void main( void ) { Composite containers[4]; for (int i=0; i < 4; i++) for (int j=0; j < 3; j++) containers[i].add( new Leaf( i * 3 + j ) ); for (i=1; i < 4; i++) containers[0].add( &(containers[i]) ); containers[0].traverse(); cout << endl; TransformVisitor tv; // don't need an "iteration" capability with this design containers[0].accept( tv ); cout << endl; } // a 0 1 2 b 3 4 5 c 6 7 8 d 9 10 11 // A 100 101 102 B 103 104 105 C 106 107 108 D 109 110 111 // Purpose. Undesireable design - double dispatch - doing the right thing based // on the type of two objects #include #include using namespace std; class Request { public: virtual string getType() = 0; }; class R1 : public Request { public: /*virtual*/ string getType() { return "One"; } void reqOneMethod( class P1* ); void reqOneMethod( class P2* ); }; class R2 : public Request { public: /*virtual*/ string getType() { return "Two"; } void reqTwoMethod( P1* ); void reqTwoMethod( P2* ); }; class Processor { public: virtual void handle( class Request* ) = 0; }; class P1 : public Processor { public: /*virtual*/ void handle( class Request* req ) { if (req->getType() == string("One")) ((R1*)req)->reqOneMethod( this ); else if (req->getType() == string("Two")) ((R2*)req)->reqTwoMethod( this ); } void procOneMethod() { cout << "processor one handling "; } }; class P2 : public Processor { public: /*virtual*/ void handle( class Request* req ) { if (req->getType() == string("One")) ((R1*)req)->reqOneMethod( this ); else if (req->getType() == string("Two")) ((R2*)req)->reqTwoMethod( this ); } void procTwoMethod() { cout << "processor two handling "; } }; void R1::reqOneMethod( P1* p ) { p->procOneMethod(); cout << "request one\n"; } void R1::reqOneMethod( P2* p ) { p->procTwoMethod(); cout << "request one\n"; } void R2::reqTwoMethod( P1* p ) { p->procOneMethod(); cout << "request two\n"; } void R2::reqTwoMethod( P2* p ) { p->procTwoMethod(); cout << "request two\n"; } void main( void ) { Processor* handlers[] = { new P1(), new P2() }; Request* commands[] = { new R1(), new R2() }; for (int i=0; i < 2; i++) for (int j=0; j < 2; j++) handlers[i]->handle( commands[j] ); } // processor one handling request one // processor one handling request two // processor two handling request one // processor two handling request two // Purpose. Visitor - double dispatch - doing the right thing based on the // type of two objects #include #include using namespace std; class Request { public: // second dispatch - the "visit()" method virtual void execute( class P1* ) = 0; virtual void execute( class P2* ) = 0; }; class R1 : public Request { public: /*virtual*/ void execute( P1* ); /*virtual*/ void execute( P2* ); }; class R2 : public Request { public: /*virtual*/ void execute( P1* ); /*virtual*/ void execute( P2* ); }; class Processor { public: // first dispatch - the "accept()" method virtual void handle( class Request* ) = 0; }; class P1 : public Processor { public: /*virtual*/ void handle( Request* req ) { req->execute( this ); } void procOneMethod() { cout << "processor one handling "; } }; class P2 : public Processor { public: /*virtual*/ void handle( Request* req ) { req->execute( this ); } void procTwoMethod() { cout << "processor two handling "; } }; /*virtual*/ void R1::execute( P1* p ) { p->procOneMethod(); cout << "request one\n"; } /*virtual*/ void R1::execute( P2* p ) { p->procTwoMethod(); cout << "request one\n"; } /*virtual*/ void R2::execute( P1* p ) { p->procOneMethod(); cout << "request two\n"; } /*virtual*/ void R2::execute( P2* p ) { p->procTwoMethod(); cout << "request two\n"; } void main( void ) { Processor* handlers[] = { new P1(), new P2() }; Request* commands[] = { new R1(), new R2() }; for (int i=0; i < 2; i++) for (int j=0; j < 2; j++) handlers[i]->handle( commands[j] ); } // processor one handling request one // processor one handling request two // processor two handling request one // processor two handling request two // Purpose. Acyclic Visitor design pattern [PLOPD vol 3, p93] // // Problem. In GOF Visitor, Element depends on Visitor, Visitor depends on // all Element derivatives, and Element derivatives depend on Element; this is // cyclic dependency. Additionally, adding an Element derivative requires the // entire Visitor hierarchy to change. "These problems can be solved by using // multiple inheritance and dynamic_cast()." // // Solution. Element derived classes are only coupled to Visitor base class. // Visitor derived classes are only coupled to the Element derived classes that // they choose to be coupled to. If a new Element derived class is added, // Visitor derived classes can update themselves if, and when, they choose. #include #include using namespace std; class Element { public: virtual void accept( class Visitor& v ) = 0; }; class This : public Element { public: /*virtual*/ void accept( Visitor& v ); string thiss() { return "This"; } }; class That : public Element { public: /*virtual*/ void accept( Visitor& v ); string that() { return "That"; } }; class TheOther : public Element { public: /*virtual*/ void accept( Visitor& v ); string theOther() { return "TheOther"; } }; class Visitor { public: virtual ~Visitor() { }; }; class ThisVisitor { public: virtual void visit( This* e ) = 0; }; class ThatVisitor { public: virtual void visit( That* e ) = 0; }; class TheOtherVisitor { public: virtual void visit( TheOther* e ) = 0; }; /*virtual*/ void This::accept( Visitor& v ) { ThisVisitor* tv = dynamic_cast( &v ); if (tv) tv->visit( this ); else cout << "the visitor was not accepted\n"; } /*virtual*/ void That::accept( Visitor& v ) { ThatVisitor* tv = dynamic_cast( &v ); if (tv) tv->visit( this ); else cout << "the visitor was not accepted\n"; } /*virtual*/ void TheOther::accept( Visitor& v ) { TheOtherVisitor* tv = dynamic_cast( &v ); if (tv) tv->visit( this ); else cout << "the visitor was not accepted\n"; } class UpVisitor : public Visitor, public ThisVisitor, public ThatVisitor, public TheOtherVisitor { /*virtual*/ void visit( This* e ) { cout << "do Up on " + e->thiss() << '\n'; } /*virtual*/ void visit( That* e ) { cout << "do Up on " + e->that() << '\n'; } /*virtual*/ void visit( TheOther* e ) { cout << "do Up on " + e->theOther() << '\n'; } }; class DownVisitor : public Visitor, public ThisVisitor, public ThatVisitor, public TheOtherVisitor { /*virtual*/ void visit( This* e ) { cout << "do Down on " + e->thiss() << '\n'; } /*virtual*/ void visit( That* e ) { cout << "do Down on " + e->that() << '\n'; } /*virtual*/ void visit( TheOther* e ) { cout << "do Down on " + e->theOther() << '\n'; } }; void main( void ) { Element* list[] = { new This(), new That(), new TheOther() }; UpVisitor up; // 4. Client creates DownVisitor down; // "visitor" objects for (int i=0; i < 3; i++) // and passes each list[i]->accept( up ); // to accept() calls for (i=0; i < 3; i++) list[i]->accept( down ); } // do Up on This // do Down on This // do Up on That // do Down on That // do Up on TheOther // do Down on TheOther // Purpose. Double dispatch (within a single hierarchy) // // Discussion. We would like to declare a function like: // void process( virtual Base* object1, virtual Base* object2 ) // that does the right thing based on the type of 2 objects that come from // a single inheritance hierarchy. The only problem is that the keyword // "virtual" may not be used to request dynamic binding for an object being // passed as an argument. C++ will only "discriminate" the type of an object // being messaged, not the type of an object being passed. So in order for // the type of 2 objects to be discriminated, each object must be the // receiver of a virtual function call. Here, when process1() is called on // the first object, its type becomes "known" at runtime, but the type of // the second is still UNknown. process2() is then called on the second // object, and the identity (and type) of the first object is passed as an // argument. Flow of control has now been vectored to the spot where the // type (and identity) of both objects are known. #include using namespace std; class Base { public: virtual void process1( Base& ) = 0; virtual void process2( class A& ) = 0; virtual void process2( class B& ) = 0; virtual void process2( class C& ) = 0; }; class A : public Base { public: /*virtual*/ void process1( Base& second ) { second.process2( *this ); } /*virtual*/ void process2( class A& first ) { cout << "first is A, second is A\n"; } /*virtual*/ void process2( class B& first ) { cout << "first is B, second is A\n"; } /*virtual*/ void process2( class C& first ) { cout << "first is C, second is A\n"; } }; class B : public Base { public: /*virtual*/ void process1( Base& second ) { second.process2( *this ); } /*virtual*/ void process2( class A& first ) { cout << "first is A, second is B\n"; } /*virtual*/ void process2( class B& first ) { cout << "first is B, second is B\n"; } /*virtual*/ void process2( class C& first ) { cout << "first is C, second is B\n"; } }; class C : public Base { public: /*virtual*/ void process1( Base& second ) { second.process2( *this ); } /*virtual*/ void process2( class A& first ) { cout << "first is A, second is C\n"; } /*virtual*/ void process2( class B& first ) { cout << "first is B, second is C\n"; } /*virtual*/ void process2( class C& first ) { cout << "first is C, second is C\n"; } }; void main( void ) { Base* array[] = { &A(), &B(), &C() }; for (int i=0; i < 3; i++) for (int j=0; j < 3; j++) array[i]->process1( *array[j] ); } // first is A, second is A // first is A, second is B // first is A, second is C // first is B, second is A // first is B, second is B // first is B, second is C // first is C, second is A // first is C, second is B // first is C, second is C // Purpose. Triple dispatch (within a single hierarchy) // // Discussion. It would be nice if C++ supported creating a function like: // "processCombine( virtual Binary& first, virtual Binary& second, virtual // Binary& third )". While not directly supported, we can simulate this kind // of capability by: calling combine() on the first object to resolve its // type, then calling combine() on the second object to resolve its type, // then calling combine() on the third object to resolve its type. We // "remember" the type information we have "discriminated" at each stage by // juggling the three objects through 2 increasingly type-specific parameter // slots. #include using namespace std; class Zero; class One; class Binary { public: // First dispatch virtual void combine( Binary& second, Binary& third ) = 0; // Second dispatch virtual void combine( Binary& third, Zero& first ) = 0; virtual void combine( Binary& third, One& first ) = 0; // Third dispatch virtual void combine( Zero& first, Zero& second ) = 0; virtual void combine( Zero& first, One& second ) = 0; virtual void combine( One& first, Zero& second ) = 0; virtual void combine( One& first, One& second ) = 0; }; class Zero : public Binary { public: void combine( Binary& second, Binary& third ) { second.combine( third, *this ); } void combine( Binary& third, Zero& first ) { third.combine( first, *this ); } void combine( Binary& third, One& first ) { third.combine( first, *this ); } void combine( Zero& first, Zero& second ); void combine( Zero& first, One& second ); void combine( One& first, Zero& second ); void combine( One& first, One& second ); void doZero() { cout << "0 "; } }; class One : public Binary { public: void combine( Binary& second, Binary& third ) { second.combine( third, *this ); } void combine( Binary& third, Zero& first ) { third.combine( first, *this ); } void combine( Binary& third, One& first ) { third.combine( first, *this ); } void combine( Zero& first, Zero& second ); void combine( Zero& first, One& second ); void combine( One& first, Zero& second ); void combine( One& first, One& second ); void doOne() { cout << "1 "; } }; void Zero::combine( Zero& first, Zero& second ) { first.doZero(); second.doZero(); doZero(); cout << endl; } void Zero::combine( Zero& first, One& second ) { first.doZero(); second.doOne(); doZero(); cout << endl; } void Zero::combine( One& first, Zero& second ) { first.doOne(); second.doZero(); doZero(); cout << endl; } void Zero::combine( One& first, One& second ) { first.doOne(); second.doOne(); doZero(); cout << endl; } void One::combine( Zero& first, Zero& second ) { first.doZero(); second.doZero(); doOne(); cout << endl; } void One::combine( Zero& first, One& second ) { first.doZero(); second.doOne(); doOne(); cout << endl; } void One::combine( One& first, Zero& second ) { first.doOne(); second.doZero(); doOne(); cout << endl; } void One::combine( One& first, One& second ) { first.doOne(); second.doOne(); doOne(); cout << endl; } void processCombine( Binary& first, Binary& second, Binary& third ) { first.combine( second, third ); } void main( void ) { Binary* list[2] = { &Zero(), &One() }; // Run through permutations for (int i=0; i < 2; i++) for (int j=0; j < 2; j++) for (int k=0; k < 2; k++) processCombine( *list[i], *list[j], *list[k] ); } // 0 0 0 // 0 0 1 // 0 1 0 // 0 1 1 // 1 0 0 // 1 0 1 // 1 1 0 // 1 1 1