让我们考虑一下。我确定您不仅有 2 个子类,所以让我们概括一下。
首先想到的是代码重复、可扩展性和紧密性。让我们扩展这些:
如果您想添加更多类,您应该在尽可能少的地方更改代码。
因为intersect
操作是可交换的,相交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
or中使用交集逻辑Sphere
,因此我们创建了一个新的class
:
struct Intersect
{
static void intersect(const Sphere&, const Plane&);
//this again with the parameters inversed, which just takes this
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)
{
//we already know object is a Sphere
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);
//finally, if no conditions were met, call intersect on 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
类中为新类型提供实现
我敢打赌,这可以进一步完善。