Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

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?

share|improve this question
add comment

4 Answers

up vote 2 down vote accepted

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;
}
share|improve this answer
    
Interesting - I'd never thought about using a visitor with overloading to achieve polymorphism. Would this overload correctly at runtime though? –  the_mandrill Feb 1 '13 at 14:30
    
Yes it would overload correctly. See edited answer –  hotpotato Feb 1 '13 at 14:36
    
I can see that this works. It's a shame though that each derived class has to implement the visit() method. –  the_mandrill Feb 1 '13 at 15:21
    
+1. I wish I discovered this pattern earlier. –  Kris Van Bael Feb 4 '13 at 6:39
    
This prompted me to do a bit more searching and I've now found the implementation of Loki's Visitor pattern which seems to be exactly what I'm looking for! I was familiar with the visitor pattern in terms of visiting nodes in trees, but hadn't thought of it in a more abstract manner. –  the_mandrill Feb 4 '13 at 14:48
add comment

The split you describe - into two parallel hierarchies - is essentially the bridge pattern.

You worry that it makes the refined abstraction (Rectangle etc.) dependent on the implementation (RectangleDrawer), but I'm not sure how you could avoid that entirely.

You can certainly provide an abstract factory to break that dependency, but you still need some way to tell the factory which subclass to create, and that subclass still depends on the specific refined implementation. That is, even if Rectangle doesn't depend on RectangleDrawer, it still needs a way to tell the factory to build one, and RectangleDrawer itself still depends on Rectangle.

It's also worth asking whether this is a useful abstraction. Is drawing a rectangle so hard that it's worth putting in a differnt class, or are you really trying to abstract the graphics library?

share|improve this answer
    
I thought I had seen that pattern before of having the parallel hierarchies - thanks for jogging my mind on that. I am though wanting to abstract the graphics library. I don't want the core library to have to depend on the graphics library. Let's say that the core application manages shape objects and the library to display them is an add-on. Bear in mind though that this really isn't an application to draw rectangles -- I'm just using it as a metaphor. –  the_mandrill Feb 1 '13 at 14:22
    
What I'm thinking is that RectangleDrawer, CircleDrawer etc. may not be the most useful way to abstract the graphics library. If, instead, you expose a set of primitives through a regular abstract interface, you still break the dependency. –  Useless Feb 1 '13 at 17:24
add comment

Have a look at Value Semantics and Concepts-based Polymorphism.

This work has changed how I look at polymorphism. We have used this system to great affect in repeated situations.

share|improve this answer
    
This looks fascinating -- seems like it's heading in the direction I'm wanting –  the_mandrill Feb 4 '13 at 13:37
add comment

The reason why you are having a problem is because objects should not draw themselves. Drawing something correctly depends on a billion pieces of state and a Rectangle is not going to have any of those pieces. You should have a central graphical object which represents the core state like backbuffer/depth buffer/shaders/etc, and then give it a Draw() method.

share|improve this answer
    
I agree that objects shouldn't draw themselves, hence the desire to move that ability into a different class to achieve a separation of concerns. Suppose these classes were Dog or Cat -- the object responsible for drawing them is a lot more complex than for drawing a rectangle. –  the_mandrill Feb 4 '13 at 9:10
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.