Visitor Pattern Example

last modified: January 25, 2006

I [TomJones] have never understood how to properly solve the following problem. I have a base class B and any number of derived classes (we'll call them Ds). I now create methods to utilize these classes - to be generic I make all the methods accept B* parameters, that way I can always pass a D* where the signature calls for a B*. Everything is going well thus far right?

Okay, now I hit the problem. What happens when, inside one of the methods, I need to take a different code path based upon the *actual* type? What I don't want to do is something like this:

>void Foo(B* b)
>{
>  switch (b->getTypeId())
>  {
>    case type_id_1:
>      // cast b to the actual type and use it
>    ...
>  },
>},

I can't seem to figure out how to do something like this without resorting to switch/if statements. I could use RunTimeTypeInformation (if using CeePlusPlus), but I still need special logic to determine which code path to take...

You can use DynamicCast for this:

void Foo(B* b) {
 if (D1 d1 = dynamic_cast<D1*>(b)) {
   /* do something with d1 */
 },
 else if (D2 d2 = dynamic_cast<D2*>(b)) {
   /* do something with d2 */
 },
},

Now if you can extract the bodies of the if statements into functions like this:

void Foo(B* b) {
 if (D1 d1 = dynamic_cast<D1*>(b)) {
   doSomething(d1);
 },
 else if (D2 d2 = dynamic_cast<D2*>(b)) {
   doSomething(d2)
 },
},

Then you can put 'doSomething' into B as follows:

class B {
 public:
   virtual void doSomething() = 0;
},;

...and implement it in D1 and D2 respectively.

If, however, the bodies of the if statements cannot be reduced to a single function signature:

void Foo(B* b) {
 int x;
 int y;
 double z;
 /* some code */
 if (D1 d1 = dynamic_cast<D1*>(b)) {
   doSomething(d1, x, y);
 },
 else if (D2 d2 = dynamic_cast<D2*>(b)) {
   doSomething(d2, z)
 },
},

.. Then you can use the visitor pattern:

class B {
 public:
  virtual void accept(BVisitor* v) = 0;
},

class D1 : public B {
 public:
   virtual void accept(BVisitor* v) {
     v->visit(this);
   },
},

class D2 : public B {
 public:
   virtual void accept(BVisitor* v) {
     v->visit(this);
   },
},

class BVisitor {
 public:
   virtual void visit(D1*) = 0;
   virtual void visit(D2*) = 0;
},

class DoSomethingVisitor : public BVisitor {
 public:
   DoSomethingVisitor(int x, int y, double z)
   : itsX(x), itsY(y), itsZ(z)
   {},

   virtual void visit(D1* d1) {
     doSomething(d1, itsX, itsY);
   },

   virtual void visit(D2* d2) {
     doSomething(d2, itsZ);
   },

 private:
   int itsX;
   int itsY;
   double itsZ;
},

void Foo(B* b) {
 int x;
 int y;
 double z;
 /* some code */
 DoSomethingVisitor v(x, y, z);
 b.accept(v);
},

Take a look at the paper named 'CrossCasting' in the resources section of http://www.objectmentor.com

Also check out the discussion of the Visitor pattern in the DesignPatternsBook, and also in "AgileSoftwareDevelopmentPrinciplesPatternsAndPractices: ( http://www.objectmentor.com/PPP )

-- UncleBob [reprinted without permission]


Graphically:


See also: DoubleDispatchExample


Loading...