Skip to main content
1 of 6
ElevenJune
  • 263
  • 1
  • 3
  • 8

Proper design to avoid the use of dynamic_cast?

After doing some researches I can not seem to find a simple example resolving a problem I encounter often.

Let's say I want to create a little application where I can create Squares, Circles, and other shapes, display them on a screen, modify their properties after selecting them, and then compute all of their perimeters.

I would do the model class like this :

class AbstractShape
{
public :
    typedef enum{
        SQUARE = 0,
        CIRCLE,
    } SHAPE_TYPE;
    
    AbstractShape(SHAPE_TYPE type):m_type(type){}
    virtual ~AbstractShape();

    virtual float computePerimeter() const = 0;
    
    SHAPE_TYPE getType() const{return m_type;}
protected :
    const SHAPE_TYPE  m_type;
};

class Square : public AbstractShape
{
public:
    Square():AbstractShape(SQUARE){}
    ~Square();

    void setWidth(float w){m_width = w;}
    float getWidth() const{return m_width;}

    float computePerimeter() const{
        return m_width*4;
    }

private :
    float m_width;
};

class Circle : public AbstractShape
{
public:
    Circle():AbstractShape(CIRCLE){}
    ~Circle();

    void setRadius(float w){m_radius = w;}
    float getRadius() const{return m_radius;}

    float computePerimeter() const{
        return 2*M_PI*m_radius;
    }

private :
    float m_radius;
};

(Imagine I have more classes of shapes : triangles, hexagones, witch each time their proprers variables and associated getters and setters. The problems I faced had 8 subclasses but for the sake of the example I stopped at 2)

I now have a ShapeManager, instanciating and storing all the shapes in an array :

class ShapeManager
{
public:
    ShapeManager();
    ~ShapeManager();

    void addShape(AbstractShape* shape){
        m_shapes.push_back(shape);
    }

    float computeShapePerimeter(int shapeIndex){
        return m_shapes[shapeIndex]->computePerimeter();
    }


private :
    std::vector<AbstractShape*> m_shapes;
};

Finally, I have a view with spinboxes to change each parameters for each type of shape. For exemple, when I select a square on the screen, the parameter widget only displays Square-related parameters (thanks to AbstractShape::getType()) and propose to change the width of the square. To do that I need a function allowing me to modify the width in ShapeManager, and this is how I do it :

void ShapeManager::changeSquareWidth(int shapeIndex, float width){
   Square* square = dynamic_cast<Square*>(m_shapes[shapeIndex]);
   assert(square);
   square->setWidth(width);
}

Is there a better design avoiding me to use the dynamic_cast and to implement a getter/setter couple in ShapeManager for each subclass variables I may have ? I already tried to use template but failed.

ElevenJune
  • 263
  • 1
  • 3
  • 8