界面臃肿需要修剪

时间:2011-07-09 00:39:58

标签: c++ design-patterns

对于糟糕的标题感到抱歉,我想不出更具描述性的标题。

我的Shape ADT越来越大,越来越膨胀,特别是由于界面的这一部分:

 
class Shape() {
    //...
    virtual bool Intersects(const Point& point) const =0;
    virtual bool Intersects(const Line& line) const =0;
    virtual bool Intersects(const Line& line, bool isInfinite) const =0;
    virtual bool Intersects(const Rectangle& rectangle) const =0;
    virtual bool Intersects(const Circle& circle) const =0;
    virtual bool Intersects(const Ellipse& ellipse) const =0;
    virtual bool Intersects(const Triangle& triangle) const =0;
    virtual bool Intersects(const Arc& arc) const =0;
    //...
};
 

每个必须将子类Shape的新类添加到接口中。这快要老了。我记得曾在某处读到有一种设计模式可以解决这个问题。模板是不可能的:每个形状都有自己独特的方式来检测与不同对象的交集,因此,它并不比目前正在进行的更好。

3 个答案:

答案 0 :(得分:4)

您遇到的问题是使用错误的功能进行任务。

你有两个对象,每个对象都有一些不确定的类型,但你知道它是在一组有限的类型上。您现在想要调用一个函数来对它们中的两个执行某些操作。并且该函数的实现将基于类型的而改变;它不仅仅基于一种类型或另一种类型。

OOP不会在这里帮助你。你需要一个不同的设计。您需要的是一系列函数,这些函数执行各种合法类型对之间的交叉。然后,使用调度函数在运行时基于这两种类型索引到此表。因此,您需要一些RTTI代码来获取类型索引或您可以用来根据分发的内容。

答案 1 :(得分:4)

首先要意识到,作为结构化,这无助于打破封装。每个交叉函数必须知道两个不同对象的内部工作。

要实现的第二件事是这是一个n ^ 2问题,或者更确切地说是n *(n + 1)/ 2。每次添加新形状时,都必须为前面的n个形状添加交点函数。显然,这不会扩展,这就是你寻求帮助的原因。

在现实世界中,处理这个问题的方法是提出一些可以作为某些形状的超集的类。例如,您的Point,Line,Rectangle和Triangle都可以表示为线段的集合;你的圆圈可以是一个360度的圆弧。如果不需要精确度,您可以将每个形状近似为Bezier曲线的集合,并使用一个具有该集合知识的Intersect方法。

答案 2 :(得分:2)

点,线,矩形,圆,椭圆,三角和圆弧都是“几何” - 你错过了一个基类。或者是常见的基类“形状”?如果是这样,你会遇到更多问题(界面Shape知道它的实现)。

我将创建一个公共基类并声明一个虚拟交叉方法。你可以使用访问者模式来实现实际的交集逻辑,而基类不需要知道所有不同的实现。