Reputation: 1850
I have a design problem I am trying to overcome. Lets assume that the class structure and hierarchy is correct, and I can not change that.
I have a class called BaseWidget. It has internal nested classes Grid, and Square. Grid maintains a vector of squares. They are there to better organise drawing behaviour. BaseWidget constructs the vector of Grid and Squares in its Layout() method.
Now I have Sub-classed BaseWidget with class DerivedWidget. Derived widget has additional drawing requirements for the class BaseWidget::Square. So it would be nice and easy to sub-class BaseWidget::Square with Derived::WidgetSquare.
The problem comes from the fact that the Grids and Squares are constructed in BaseWidget::OnLayout. So when the Grid vector of squares is created I would like to say "use DerivedSquare instead of Square in the vector you are populating".
I believe my solution is to use templates but I am not sure in what capacity.
class BaseWidget : public Widget
{
protected:
void Layout(void)
void Draw(canvas c);
class Square {
Square(Rect r) : region(r) {}
Rect region;
virtual void Draw(canvas c);
};
class Grid {
std::std::vector<shared_ptr<<Square>> squares;
Rect region;
void Draw(canvas c);
};
std::vector<Grid> m_grids;
};
void Parent::Layout {
m_grids.resize(num_grids);
for (int i=0; i<num_grids; ++i) {
m_grids[i].region = some_rect;
for (int j=0; j<num_squares; ++j) {
m_grids[i].m_squares.push_back(std::make_shared<Square>(some_other_rect));
}
}
}
void BaseWidget::Draw(Canvas c)
{
for (int i = 0; i < m_grids.size(); i++) {
m_grids.Draw(c);
}
}
void Grid::Draw(Canvas c)
{
// draw some stuff here
for (int i = 0; i < m_squares.size(); i++) {
m_squares[i].Draw(c);
}
}
void Square::Draw(Canvas c)
{
// draw some stuff here
}
// new class that extends parent
class DerivedWidget : public BaseWidget
{
protected:
/*
* This class wants to do some special drawing in addition to its parents drawing
*/
class DerivedSquare : public Square {
virtual void Draw(canvas c);
};
}
Upvotes: 0
Views: 84
Reputation: 217135
It seems you just need a factory, you can add a virtual method for that:
class BaseWidget : public Widget
{
protected:
void Layout()
void Draw(canvas c);
class Square {
public:
Square(Rect r) : region(r) {}
Rect region;
virtual void Draw(canvas c);
};
class Grid {
public:
std::std::vector<shared_ptr<<Square>> squares;
Rect region;
void Draw(canvas c);
};
virtual std::unique_ptr<Square> MakeSquare() const { return std::make_unique<Square>(/*...*/); }
std::vector<Grid> m_grids;
};
// new class that extends parent
class DerivedWidget : public BaseWidget
{
protected:
/*
* This class wants to do some special drawing in addition to its parents drawing
*/
class DerivedSquare : public Square {
virtual void Draw(canvas c);
};
std::unique_ptr<Square> MakeSquare() const override { return std::make_unique<DerivedSquare>(/*...*/); }
}
And use MakeSquare()
in your code instead of hard coding Square
.
Upvotes: 1
Reputation: 1135
Try something like this:
template <class Derived>
class BaseWidget : public Widget
{
// ...
class Grid {
std::std::vector<shared_ptr<<Derived::SquareType>> squares;
// ...
}
}
class DerivedWidget : public BaseWidget
{
using SquareType = DerivedSquare;
// ...
}
Upvotes: 0
Reputation: 2423
If I understood your question correctly, you are looking for something like the following. Note that I simplified the classes and methods, it should serve as just a proof of concept:
struct BaseWidget
{
struct BaseSquare
{
virtual void draw()
{
// ... Drawing Logic for Base Square
}
};
virtual void draw()
{
drawInternal<BaseSquare>();
}
protected:
template <typename SquareType>
void drawInternal()
{
auto squareToDraw = std::make_shared<SquareType>();
squareToDraw->draw();
}
};
struct DerivedWidget : BaseWidget
{
struct DerivedSquare : BaseSquare
{
virtual void draw() override
{
// Drawing Logic for Dervied Square
}
};
virtual void draw() override
{
drawInternal<DerivedSquare>();
}
};
Of course this has some important limitations. For one, the BaseWidget
class must know how to construct the squares, so the DerivedSquare
cannot have a fancy constructor. You could potentially overcome this by also passing constructor arguments to the drawInternal
function but I am unsure if that fits in your design.
Upvotes: 1