Стандартное решение этой проблемы, особенно с учетом ваших ограничений относительно зависимостей, заключается в использовании Шаблон посетителя .
Вот как шаблон посетителя будет работать в вашем случае:
- Вам нужен абстрактный
ShapeVisitor
класс. У него есть абстрактный Visit
метод для каждого конкретного подкласса Shape. например: Visit(Circle*)
, Visit(Square*)
и т. д.
- Форма имеет абстрактный метод
AcceptVisitor(ShapeVisitor*)
.
- Каждый подкласс Shape реализует
AcceptVisitor
как просто вызов visitor->Visit(this)
- Каждый класс
CAD
является (или имеет до вас) ShapeVisitor
. Методы Visit
делают соответствующий рисунок для определенного типа Shape
. Никаких условий или кастинга не требуется.
Вот модифицированная версия вашего кода, которая использует шаблон посетителя довольно слабо:
class Circle;
class Square;
class ShapeVisitor
{
virtual void Visit(Circle *circle) = 0;
virtual void Visit(Square *square) = 0;
}
class Shape
{
virtual void AcceptVisitor(ShapeVisitor *visitor) = 0;
}
class Circle : public Shape
{
// some special members/methods
virtual void AcceptVisitor(ShapeVisitor *visitor)
{
visitor->Visit(this);
}
}
class Square : public Shape
{
// some special members/methods
virtual void AcceptVisitor(ShapeVisitor *visitor)
{
visitor->Visit(this);
}
}
class CAD : public ShapeVisitor
{
virtual DrawCircle(Circle *circle) = 0;
virtual DrawSquare(Square *square) = 0;
virtual void Visit(Circle *circle) {
DrawCircle(circle);
}
virtual void Visit(Square *square) {
DrawSquare(square);
}
}
class SWX : public CAD
{
virtual DrawCircle(Circle *circle){// do some stuff that draws circle on SWX system}
}
class PRO : public CAD
{
virtual DrawCircle(Circle * circle){// do some stuff that draws circle on PRO system}
}
int main()
{
Circle * circle = new Circle();
Square * sq = new Square;
vector<Shape*> shapes;
shapes.push_back(circle);
shapes.push_back(sq);
SWX * swx = new SWX();
for( int i = 0 ; i < shapes.size() ; ++i )
{
shapes[i]->AcceptVisitor(SWX);
}
}
В этом коде я решил сделать CAD
фактически подклассом ShapeVisitor
. Кроме того, поскольку у вас уже есть виртуальные методы в CAD
для рисования, я реализовал методы Visit
там (один раз), а не один раз в каждом подклассе. Как только вы переключаете клиентов на использование AcceptVisitor
вместо непосредственного вызова методов Draw *, вы можете защитить эти методы, а затем в конечном итоге перенести реализацию методов Visit
вниз на подклассы (то есть рефакторинг для удаления дополнительный уровень косвенности вызван наличием Visit(Foo*)
call DrawFoo(Foo*)
).