让我们考虑一下这个问题。我确信您不仅有 2 个子类,所以让我们概括一下。
首先想到的是代码重复、可扩展性和封闭性。让我们扩展一下这些:
如果您想添加更多类,您应该在尽可能少的地方更改代码。
因为intersect
操作是可交换的,相交的代码A
and B
应该与相交代码位于同一位置B
and 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
新类型类
我打赌这可以进一步完善。