阶级关系:需要建筑建议

Relations of Classes: Architectural Advice Needed

本文关键字:建筑 阶级关系      更新时间:2023-10-16

假设我们有一些类,它们之间有关系。例如,几何或数学库有Vector3、Matrix4、Plane3等。它们之间有很多交集测试方法。例如,测试Plane3和Vector3之间的交叉点;如果Vector3(作为点)在平面上,在平面外的后面和平面外的前面。。。等

因此,交集测试方法既可以写在Vector3类上,也可以写在Plane3类上。但这会导致一点复杂和重复的硬编码。对于这种情况,有什么建议吗。

  • 在一个更有意义的类上实现该方法,特别是在Plane3上,因为它更多地用于交集,它的目的是交集测试和诸如此类的事情
  • 在两个类上都实现
  • 在实用程序类中实现为静态方法
  • 其他

第一种情况可能很好,但有时情况并不像它所说的那样清楚。第二种需要更多的重复代码,特别是对于很多方法的类,这会导致代码的笛卡尔乘法增加。第三个可能很好,但有时它无法访问类的私有或受保护的方法(如果不是作为朋友实现的话),而且我通常不知道如何对实用程序类进行分类,用户很难知道他/她要找的方法在哪里。那么,对于这种情况有什么缓解措施吗?

编辑:更详细的例子。

class A {}
class B {}
class C {}

第一:

bool A::IsIntersecting(const B& b) const;
bool A::IsIntersecting(const C& c) const;
bool B::IsIntersecting(const C& c) const;

第二:

bool A::IsIntersecting(const B& b) const;
bool A::IsIntersecting(const C& c) const;
bool B::IsIntersecting(const A& a) const;
bool B::IsIntersecting(const C& c) const;
bool C::IsIntersecting(const A& a) const;
bool C::IsIntersecting(const B& b) const;

第三:

bool IntersectUtility::IsIntersecting(const A &a, const B &b);
bool IntersectUtility::IsIntersecting(const A &a, const C &c);
bool IntersectUtility::IsIntersecting(const B &b, const C &c);

如果您有一个跨多个类操作的函数,您可以考虑将其作为一个独立的函数。记住:不是每个函数都需要与一个类相关联(除非您使用的是Java或C#)。因此,你可以有这样的东西:

bool intersects(const T1 &a, const T2 &b);

接下来要考虑的是什么关系是可能的和有意义的(例如,询问向量是否与矩阵相交是没有意义的)。这将告诉您应该将哪些类组合与该方法一起使用。

接下来,考虑方法中的等价性。如果是A op B == B op A,那么你可以写:

inline bool intersects(const Vector3 &a, const Plane3 &b)
{
    return intersects(b, a);
}

这是在实现关系运算符时使用的——您可以根据==实现!=,根据<实现>>=<=

这也适用于构图(例如,在Point2D对象的xy坐标上调用intersects)。

如果你在写一个库,你应该先写最常见/最有用的组合,并以一个全面的集合为目标(在它们有意义的地方)。如果你正在编写一个应用程序,你应该专注于提供所需的东西。

Implement the method on one class which has more meaning to has it, specically on Plane3 because it is used more for intersections, its aim is, intersection test and this kind of things.

这种解决方案";气味";对我来说,有几个原因。我看到的最大问题是,当消费者试图找出一对特定几何对象的intersectionTest函数的位置时,这将非常令人困惑。例如,它在Line还是Point中?如果LinePlane相交,测试如何?我可以看到选择任意规则的诱惑,比如高维对象包含与低维对应的函数,但当你测试相同维度的对象时,你会遇到同样的问题。

Implement on both classes.

这是太多的重复代码。但是,您可以创建1D、2D、3D等的基本实现,从而完成大部分工作。这显然会利用继承,而你说你不想这么做。

Implement in a utility class as a static method.

这没有一个明显的解决方案的原因是,这些对象中的两个(或更多个,你似乎不想考虑)的交集并不真正属于它们中的任何一个。它属于";空间";所以我的建议是少考虑一个对象与另一个对象相交的问题,而是根据它们在";空间",无论是1D空间、2d空间等。最终,是将其实现为裸函数还是静态函数、基类,还是容纳对象的容器,都取决于您。

我建议将isIntersecting作为一组非成员函数,与ABC:位于同一命名空间中

namespace X {
  class A { };
  class B { };
  class C { };
  bool isIntersecting(const A&, const B&);
  bool isIntersecting(const A&, const C&);
  bool isIntersecting(const B&, const C&);
}

只有在必要的时候才让他们成为朋友。