Visitor design pattern - breaking dependence on the target hierarchy

S

Siphiuel

Hi everyone.
When using visitor pattern, we have a nasty dependence on the types of
visitable objects that is coded way on top on the visitor hierarchy. i
mean, like this:

class AbstractVisitor
{
public:
virtual void visit(Object_type_1 *);
virtual void visit(Object_type_2 *)
.............................
virtual void visit(Object_type_N *;
};

That is so nice that visitable objects are aware only of the
AbstractVisitor type, but that's not so good that every concrete
visitor must know about the whole multitude of visitable object types!

I've encountered this problem when trying to separate visitor/visitable
classes into libraries. That is, suppose there is a base library Base,
that contains definitions for AbstractVisitor and some AbstractObject
class, which is the base class for all visitable objects. Pointers to
AbstractObject's are stored in some STL container inside the Base
library. Objects can be manipulated by visitors only through some
method in the container class:

// ------ Classes contained in the Base library ------
// Abstract.h
class AbstractVisitor;
class AbstractObject
{
public:
void accept(AbstractVisitor *);
};

// Container.h
#include "AbstractVisitor.h"
#include "AbstractObject.h"

class Container
{
private:
std::set<AbstractObject *> objects_;.
public:
void manipulate(AbstractObject *, AbstractVisitor *);
};

There could be multiple client libraries that have their specific
AbstractObject descendants and visitors, which are run only on the
objects defined in the same library. For instance:

// ------ Classes contained in ClientLibrary1 ------
class ClientObject1
:
public AbstractObject
{
public:
void accept(AbstractVisitor *);
};

class ClientVisitor1
:
public AbstractVisitor
{
public:
// It is desirable that here are contained only methods pertaining to
relevant objects
void visit(ClientObject1 *);
// other visit()'s
};

Naturally, it is not good for ClientLibrary1 to know about object types
contained in some ClientLibrary2, for instance.

The aim can be achieved through the use of dynamic_cast'ing, though. In
order to accomplish, we modify the visitor object hierarchy slightly by
inserting a templatized ancestor class:

// ------ Base library code ------
class AbstractVisitor
{
public:
// AbstractVisitor now knows nothing about the objects
// on which it's descendants operate
virtual void visit(void *); // default method
};

template<class TObjectType>
class InterimVisitor
:
virtual public AbstractVisitor
{
public:
virtual void visit(TObjectType *);
};

The concrete visitor classes can now be defined as follows:

// ------ Client library code
class ConcreteVisitor
:
public InterimVisitor<ObjectType1>,
public InterimVisitor<ObjectType2>
{
public:
virtual void visit(ObjectType1 *);
virtual void visit(ObjectType2 *);
};

And finally, the use of dynamic_cast does the right job in overloaded
accept methods:

class SomeObject
:
public AbstractObject
{
public:
void accept(AbstractVisitor * v)
{
Interim<SomeObject> * i = dynamic_cast<Interim<SomeObject> *>(v);
if (i)
{
// v is allowed to visit this object
v->visit(this);
}
}
};

I'm sorry for including this much of code and statements that you all
probably have seen thousand times :). And the main question is - are
there any other solutions that do not incur performance penalties
caused by the use of dynamic_cast? Thanks in advance
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,904
Latest member
HealthyVisionsCBDPrice

Latest Threads

Top