让我们仔细考虑一下。我相信你不仅有两个子类,所以让我们将其概括一下。首先想到的是代码重复、可扩展性和紧密性。让我们详细讨论一下:
如果你想添加更多类,应该尽可能少地修改代码。因为交集操作是可交换的,交错处理A和B的代码应该与交错处理B和A的代码放在同一个位置,所以把逻辑保留在类本身内部是不可能的。
此外,添加新类不应意味着你必须修改现有类,而应该扩展委托类(是的,我们要涉及模式了)。
下面是当前的结构(或类似的结构,可能是intersect的返回类型,但现在不重要):
struct Primitive
{
virtual void intersect(Primitive* other) = 0;
};
struct Sphere : Primitive
{
virtual void intersect(Primitive* other)
};
struct Plane : Primitive
{
virtual void intersect(Primitive* other);
};
我们已经决定不要将交点逻辑放在Plane
或Sphere
中,因此我们创建了一个新的class
:
struct Intersect
{
static void intersect(const Sphere&, const Plane&);
static void intersect(const Sphere&, const Sphere&);
static void intersect(const Plane&, const Plane&);
};
这是你将添加新功能和新逻辑的类。例如,如果您决定添加一个
Line
类,只需添加方法
intersec(const Line&,...)
。
请记住,在添加新类时,我们不想更改现有代码。因此,我们无法在您的交点函数中检查类型。
为此,我们可以创建一个行为类(策略模式),根据类型表现出不同的行为,并在以后进行扩展:
struct IntersectBehavior
{
Primitive* object;
virtual void doIntersect(Primitive* other) = 0;
};
struct SphereIntersectBehavior : IntersectBehavior
{
virtual void doIntersect(Primitive* other)
{
Sphere& obj1 = (Sphere&)*object;
if ( dynamic_cast<Sphere*>(other) )
return Intersect::intersect(obj1, (Sphere&) *other);
if ( dynamic_cast<Plane*>(other) )
return Intersect::intersect(obj1, (Plane&) *other);
return other->intersect(object);
}
};
在我们原来的方法中,我们会有:
struct Sphere : Primitive
{
virtual void intersect(Primitive* other)
{
SphereIntersectBehavior intersectBehavior;
return intersectBehavior.doIntersect(other);
}
};
一个更清晰的设计是实现一个工厂,来抽象出行为的实际类型:
struct Sphere : Primitive
{
virtual void intersect(Primitive* other)
{
IntersectBehavior* intersectBehavior = BehaviorFactory::getBehavior(this);
return intersectBehavior.doIntersect(other);
}
};
如果您按照这个设计,甚至不需要intersect
成为虚拟的,因为它会为每个类执行此操作。
如果您遵循这个设计:
- 添加新类时无需修改现有代码
- 将实现放在一个地方
- 仅为每种新类型扩展
IntersectBehavior
- 在
Intersect
类中为新类型提供实现
我敢打赌,这甚至可以进一步完善。