我有以下C ++代码(简化版):
class Shape
{
bool isCircle = false;
bool isSquare = false;
}
class Circle : public Shape
{
// some special members/methods
}
class Square : public Shape
{
// some special members/methods
}
class CAD
{
virtual DrawCircle(Circle * circle) = 0;
}
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();
circle->isCircle = true;
Square * sq = new Square;
sq->isSquare = true;
vector<Shape*> shapes;
shapes.push_back(circle);
shapes.push_back(sq);
SWX * swx = new SWX();
for( int i = 0 ; i < shapes.size() ; ++i )
{
if( shapes[i]->isCircle )
{
SWX->DrawCircle((Circle*)(shapes[i]));
}
else if( shapes[i]->isSquare )
{
SWX->DrawSquare((Square*)(shapes[i]));
}
}
我希望不再需要if ... else(如果可能的话,在下面所述的限制范围内)。
我现在的约束是:
欢迎您提出任何建议/意见/解决方案。
答案 0 :(得分:8)
此问题的标准解决方案,特别是考虑到您对依赖关系的限制,是使用Visitor Pattern。
以下是访客模式在您的情况下如何运作:
ShapeVisitor
课程。它为Shape的每个具体子类都有一个抽象的Visit
方法。例如:Visit(Circle*)
,Visit(Square*)
等AcceptVisitor(ShapeVisitor*)
方法。AcceptVisitor
只需调用visitor->Visit(this)
CAD
班级都是({a-a,由你决定)a 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*)
调用DrawFoo(Foo*)
)导致的额外间接级别。
答案 1 :(得分:6)
这是DoubleDispatch的经典案例,您需要为每个可能的(形状,CAD)对分别设置一个方法:
isSquare
/ isCircle
字段。virtual void DrawOn(CAD*)
添加到Shape
界面。实施Circle::DrawOn(CAD*)
(例如):
void Circle::DrawOn(CAD *c) {
c->DrawCircle(this);
}
这是一个“技巧”,它允许像myCircle->DrawOn(mySWX)
这样的调用调用正确的方法,无论Shape或CAD的类型如何。
答案 2 :(得分:1)
你有一些相当不稳定的OO,但至少DrawXxxx应该成为Draw()。然后,Circle,Square和其他形状将定义Draw()方法,该方法为Shape上的虚拟Draw方法提供实现。然后你可以在任何形状上调用Draw,它会做正确的事情。
isXxxx布尔也应该去。类知道它们是什么,instanceof可以告诉你(虽然没有必要检查你何时绘制,因为这将是一个虚方法调用)。
答案 3 :(得分:1)
为什么不只是SWX-&gt; Draw(shapes [i]);?您需要添加两个Draw方法,其中一个采用Circle,另一个采用方形?
答案 4 :(得分:1)
这是非常基本的OO多态性。圆形和方形是形状的专用版本。他们每个人都应该知道处理你的cad课程需要什么样的专业行为。
class Shape
{
virtual void DrawWithCAD(CAD * cad) = 0;
}
class Circle : public Shape
{
virtual void DrawWithCAD(CAD * cad)
{
cad->DrawCircle(this);
}
}
class Square : public Shape
{
virtual void DrawWithCAD(CAD * cad)
{
cad->DrawSquare(this);
}
}
然后你的main()循环将变为:
for( int i = 0 ; i < shapes.size() ; ++i )
{
shapes[i]->DrawWithCAD(swx);
}
答案 5 :(得分:1)
为什么不定义一个简单的ICAD接口?由于CAD取决于Shape,因此不会增加复杂性:
class Shape
{
Draw(ICAD* cad) = 0;
}
class Circle : public Shape
{
Draw(ICAD* cad)
{
ICAD->DrawCircle(self)
}
}
class Square : public Shape
{
Draw(ICAD* cad)
{
ICAD->DrawSquare(self)
}
}
DrawSquare(self)看起来很有趣,但我不知道CAD类对形状对象的作用。
class ICAD
{
virtual DrawSquare(Square* square) = 0;
virtual DrawCircle(Circle * circle) = 0;
}
我认为CAD类比那些抽象方法更多,这就是为什么你不把它与Shape结合起来。
class CAD : public ICAD
{
// big CAD class...
}
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]->Draw(swx);
}
}
答案 6 :(得分:0)
这可能不是理想的解决方案,但您可以提供CAD :: Draw成员函数并重载它以处理每种不同类型的形状。类似的东西:
class CAD {
public:
virtual void Draw(const Circle& circle) = 0;
virtual void Draw(const Square& square) = 0;
};
然后,你可以在任何支持的对象上调用Draw
,而不需要任何if
语句。
答案 7 :(得分:0)
我要解决这个问题的方法是让各个形状类将自己绘制到某种绘图“canvas”(一个提供绘图缓冲区和一些基本绘图函数的类,例如直线,矩形和椭圆)。 / p>
然后SWX
类会调用Shape
的{{1}}方法,为其提供自己的画布:
Draw
答案 8 :(得分:0)
为每个Shape重写多个全局Draw函数...(DrawCircle和朋友是虚拟的,所以我们可以使用多态来调用CAD对象)
void Draw(CAD *cad, Circle *circle){
cad->DrawCircle(circle);
}
void Draw(CAD *cad, Square *square){
cad->DrawSquare(square);
}