tags:

views:

85

answers:

1

Hi everybody,

I'm making the design of a OO framework and I'm facing the following problem.

Let's say that in the framework I have a Shape interface and the users are free to implement and extends (adding new functions) the Shape interface to create their own figures, e.g. Square and Circle. To make these new objects available the users have to register them into a ShapeFactory specifying the name of the shape (string) and the object.

Furthermore, the framework provides an interface called ShapeWorker which defines the following function:

class ShapeWorker
{
public:
  void processShape( Shape& shape ) = 0;
};

The users are free to implement the ShapeWorker interface to make specific shape worker, e.g. SquareWorker and CircleWorker. To make these new objects available the users have to register them into a WorkerFactory, specifying the name of shape (string) and the object.

At a certain point, the framework, given a string representing the shape's name, creates a new Shape, by using the ShapeFactory, and afterwards (somewhere else in the code) creates a new ShapeWorker, by using the WorkerFactory with the same shape's name. The processShape is then called providing the Shape instance created before.

[ ... ]
Shape* myShape = shapeFactory.create( shapeName );
[ ... ]
ShapeWorker* myWorker = workerFactory.create( shapeName );
myWorker->processShape( *myShape );
[ ... ]

The point is that, doing so, I force the user implementing, for example, the SquareWorker to make a down-cast from Shape to Square into the processShape function so to access to the full Square's interface:

class SquareWorker
{
public:
  void processShape( Shape& shape )
  {
     Square& square = dynamic_cast< Square& >( shape );
     // using Square interface
  }
};

This is against the Liskov substitution principle.

Now, is this approach wrong? What would it be the better solution? Note that I don't want to implement the processShape as Shape's member function.

I hope the description has been clear enough.

Thanks in advance for your help.

Simo

+2  A: 

Unless your shapes have a common interface that must be used by the workers, this approach seems fully correct to me. A shape worker is more or less specialized on a specific shape, thus has knowledge about the class it handles. It would be nicer to do that using a common interface for all shapes but you cannot put everything you would need into it, it would end up fully cluttered. Downcasting is a correct mean to solve this.

The use of templates could help you out: you could create a base class for all workers

template <class T> 
class BaseShapeWorker : ShapeWorker
{
public:
  void processShape( Shape& shape )
  {
     T& specificShape = dynamic_cast< T& >( shape );
     processShape( specificShape )
  }
protected:
  virtual void processShape( T& shape ) = 0;
};

This would not need the implementers to know about this downcast and ease the implementation by maybe also providing some often reused functionality.

jdehaan
+1. NVI is a nice pattern to use :)
David Rodríguez - dribeas
Thank you jdehaan, this is a great piece of advice! It would simplify the work for the implementers.
Simone