C++ Design Patterns – Polymorphic Behaviour with Library Separation

cdesign-patternspolymorphism

Let's say I have a hierarchy of Item classes: Rectangle, Circle, Triangle. I want to be able to draw them, so my first possibility is add a virtual Draw() method to each:

class Item {
public:
   virtual ~Item();
   virtual void Draw() =0; 
};

However, I want to split the drawing functionality to a separate Draw library while the Core library contains only the basic representations. There's a couple of possibilities I can think of:

1 – A DrawManager that takes a list of Items and has to use dynamic_cast<> to work out what to do:

class DrawManager {
  void draw(ItemList& items) {
    FOREACH(Item* item, items) {
       if (dynamic_cast<Rectangle*>(item)) {
          drawRectangle();
       } else if (dynamic_cast<Circle*>(item)) {
          drawCircle();
       } ....
    }
  }
};

This isn't ideal as it relies on RTTI and forces one class to be aware of all items in the hierarchy.

2 – The other approach is to defer drawing responsibility to an ItemDrawer hierarchy (RectangleDrawer, etc):

class Item {
   virtual Drawer* GetDrawer() =0;
}

class Rectangle : public Item {
public:
   virtual Drawer* GetDrawer() {return new RectangleDrawer(this); }
}

This achieves the separation of concerns between the base representation of the Items and the code for drawing. The problem though is that the Item classes are dependent on the drawing classes.

How can I separate out this drawing code into a separate library? Is the solution for the Items to return a factory class of some description? However, how can this be defined so that the Core library isn't dependent on the Draw library?

Best Answer

Take a look at the visitor pattern.

Its intention is to separate an algorithm (in your case drawing) from an object structure on which it operates (items).

So a simple example for your problem would be :

class Item
{
public:
    virtual void visit(Visitable v)
    {
        v.accept(*this)
    }
};

class Rectangle : public Item
{
public:
    virtual void visit(Visitable v)
    {
        v.accept(*this)
    }
};

class Circle : public Item
{
public:
    virtual void visit(Visitable v)
    {
        v.accept(*this)
    }
};


class Visitable
{
public:
    virtual void accept(Rectangle& r);
    virtual void accept(Circle& c);
};

class Drawer : public Visitable
{
public:
    void accept(Rectangle& r)
    {
        // draw rectangle
    }   
    void accept(Circle& c)
    {
        // draw circle
    }
};


int main()
{
    Item* i1 = new Circle;
    Item* i2 = new Rectangle;

    Drawer d;
    i1->visit(d); // Draw circle
    i2->visit(d); // Draw rectangle

    return 1;
}