解决这种多态性问题的最优雅方式

Bra*_*iub 8 c++ polymorphism

编辑:我正在使用C++.

所以,我正在创建方法/函数来测试形状之间的交集.我基本上有这个:

class Shape {};

class Rectangle : public Shape {};

class Circle : public Shape {};

class Line : public Shape {};
Run Code Online (Sandbox Code Playgroud)

现在,我需要决定编写实际方法/函数来测试交集的最佳方法.但是我的所有形状都将存储在Shape指针列表中,因此我将调用基本形式的方法/函数:

bool intersects (Shape* a, Shape* b);
Run Code Online (Sandbox Code Playgroud)

那时,我需要确定哪种类型的形状'a'和'b',所以我可以正确地检测碰撞.我只需使用一些虚拟方法就可以轻松完成其中一个:

class Shape
{
    virtual bool intersects (Shape* b) = 0;
}
Run Code Online (Sandbox Code Playgroud)

这将确定其中一个形状('a'现在是'this').但是,我仍然需要获得'b'的类型.显而易见的解决方案是给Shape一个'id'变量来分类它是什么形状,然后'切换'通过那些,然后使用dynamic_cast.然而,这不是很优雅,感觉应该有更多的OO方式来做到这一点.

有什么建议?

Mat*_* M. 7

正如@Mandarse所指出的,这是典型的双重调度问题.在面向对象的语言中,或者像可以实现面向对象概念的C++语言一样,这通常使用访问者模式来解决.

通常,Visitor接口本身定义了每种具体类型的一个回调.

class Circle;
class Rectangle;
class Square;

class Visitor {
public:
  virtual void visit(Circle const& c) = 0;
  virtual void visit(Rectangle const& r) = 0;
  virtual void visit(Square const& s) = 0;
};
Run Code Online (Sandbox Code Playgroud)

然后,Shape层次结构适用于此.我们需要两种方法:一种是接受任何类型的访问者,另一种是创建"适当的"交叉访问者.

class Visitor;
class Intersecter;

class Shape {
public:
  virtual void accept(Visitor&) const = 0; // generic
  virtual Intersecter* intersecter() const = 0;
};
Run Code Online (Sandbox Code Playgroud)

交叉点很简单:

#include "project/Visitor.hpp"

class Intersecter: public Visitor {
public:
  Intersecter(): result(false) {}
  bool result;
};
Run Code Online (Sandbox Code Playgroud)

例如,对于Circle,它将给出:

#include "project/Intersecter.hpp"
#include "project/Shape.hpp"

class Circle;

class CircleIntersecter: public Intersecter {
public:
  explicit CircleIntersecter(Circle const& c): _left(c) {}

  virtual void visit(Circle const& c);    // left is Circle, right is Circle
  virtual void visit(Rectangle const& r); // left is Circle, right is Rectangle
  virtual void visit(Square const& s);    // left is Circle, right is Square

private:
  Circle const& _left;
}; // class CircleIntersecter


class Circle: public Shape {
public:
  virtual void accept(Visitor& v) const { v.visit(*this); }

  virtual CircleIntersecter* intersecter() const {
    return new CircleIntersecter(*this);
  }
};
Run Code Online (Sandbox Code Playgroud)

用法:

#include "project/Intersecter.hpp"
#include "project/Shape.hpp"

bool intersects(Shape const& left, Shape const& right) {
  boost::scope_ptr<Intersecter> intersecter(left.intersecter());
  right.accept(*intersecter);
  return intersecter->result;
};
Run Code Online (Sandbox Code Playgroud)

如果其他方法需要双重调度机制,那么您需要做的就是创建另一个"类似Intersecter"的类,它包装结果并继承自,Visitor并且一个新的"Factory"方法Shape根据派生类重写,以提供适当的操作.这有点啰嗦,但确实有效.

注:这是合理的除外intersect(circle, rectangle),并intersect(rectangle, circle)产生相同的结果.您可以将代码分解为某些方法,并将CircleIntersecter::visit代理委托给具体实现.这可以避免代码重复.