C++ 类的关系:需要架构建议

C++ 类的关系:需要架构建议,c++,design-patterns,C++,Design Patterns,假设我们有一些类,它们之间有关系。例如,几何体或数学库具有Vector3、Matrix4、Plane3..等。它们之间有很多交叉测试方法。例如,测试Plane3和Vector3之间的交点;如果矢量3(作为点)在平面上、平面外背面和平面外正面…等等 因此,可以在Vector3和Plane3类上编写相交测试方法。但这会导致少量的复杂性和硬编码的重复。对于这种情况有什么建议吗 在一个更具意义的类上实现该方法,特别是在Plane3上,因为它更多地用于交叉口,其目的是交叉口测试和此类事情 在两个类上实现

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

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

  • 在一个更具意义的类上实现该方法,特别是在Plane3上,因为它更多地用于交叉口,其目的是交叉口测试和此类事情
  • 在两个类上实现
  • 作为静态方法在实用程序类中实现
  • 其他
第一种情况可能是好的,但有时情况并不像它所说的那样清楚。第二种方法需要更多的重复代码,特别是对于具有大量方法的大量类,会导致代码的笛卡尔乘法增加。第三种方法可能很好,但有时它无法访问类的私有或受保护的方法(如果不是作为friend实现的话),而且我通常不知道如何对实用程序类进行分类,用户更难知道他/她正在寻找的方法在哪里。那么对于这种情况有什么办法吗

编辑:更详细的例子

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);
这个解决方案对我来说“有味道”,原因有几个。我所看到的最大问题是,当消费者试图找出一对特定几何对象的
intersectionTest
函数的位置时,会非常混乱。例如它是在
还是
?如果
线
平面
相交,测试如何?我可以看到一种选择任意规则的诱惑,比如高维对象包含与低维对象对应的函数,但是当您测试相同维的对象时,您会遇到同样的问题

Implement on both classes.
这是太多重复的代码。但是,您可以为1D、2D、3D等创建一个基本实现来完成大部分工作。这显然会利用继承,而你说你不想这样做

Implement in a utility class as a static method.

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

如果你有一个跨多个类运行的函数,你可以考虑使它成为一个独立的函数。记住:不是每个函数都需要与类关联(除非您使用的是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);
}

这在实现关系运算符时使用——您可以实现
=
==
=
而言,我建议在
a
B
C
相同的名称空间中设置一组非成员函数:

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&);
}

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

如果使用多态性或仅使用继承,问题就完全不同了。你属于哪一类问题?这与多态性或遗传无关。假设不使用它们。因此,在最简单的情况下,最好的方法是第三个解决方案:外部函数知道所有对象。我也在考虑第三个解决方案,第一个解决方案,但不是第二个。我认为,当方向很重要时,第二条就有意义了。比如,狗:咬(人);人类::喂养(狗);这个建筑问题总是让我在设计中感到困惑。特别是在游戏中特别定义对象关系。谢谢你的建议。看来你的建议看起来好多了。谢谢你的建议。实际上,如果没有性能损失,我更喜欢尽可能多地使用类。也许从C#开始就习惯了,但我可以更协调地使用名称空间。第二条建议;当然,向量不与矩阵相交,我从来没有提到过这种情况。当然,在同一类库(D、E、F…)中还有一些其他类,但不需要查看它们之间的交叉点。
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&);
}