在这种情况下代码结构的最佳实践方法?

时间:2010-09-19 12:41:19

标签: c# design-patterns

嘿,我对代码结构有疑问,并且想知道最佳做法是什么,或者我是否应该使用特定的设计模式。

我有一个抽象基类BoundingVolume,它可以有子类,如BoundingSphere,BoundingBox等。必须在两个BoundingVolumes之间进行比较,以确定它们是否重叠,这个比较是由一个跟踪所有的世界类进行的。模拟中的对象,负责处理卷之间的冲突。

我最初的想法是,World类将包含BoundingVolume对象的集合,并调用BoundingVolume上定义的虚拟CollidesWith方法,该方法将接受另一个BoundingVolume进行比较。问题是用于确定每个子类的两个BoundingVolumes重叠是否不同的算法,例如球体与球体碰撞的算法与框与盒子碰撞的算法不同,或者盒子与球体碰撞。

使用我的初始计划,每个子类必须确定传递给它的实际类型的BoundingVolume是什么,并切换到使用正确的算法。此外,每次添加新子类时都必须扩展每个子类,从而有效地消除了首先拥有子类的所有好处。有没有更好的方法来构建它?

我更倾向于寻找这种情况的解决方案,而不是对这种特定情况的回答。

感谢您的帮助。

3 个答案:

答案 0 :(得分:0)

这似乎是一个单独的功能听起来像最好的解决方案的情况。创建一个函数,它是子类的朋友,因此它可以占用它的成员。要确定传递给它的子类,它可以读取私有/受保护的成员函数className()。

这样,您只需要为所有可能的边界形状组合进行切换。我会给每个形状一个数字。这将是一个switch语句,因此您可以获得最高和最低。这是为了避免公式重复(A == B = B == A)。

我希望我有道理。我在iPhone上输入这个

答案 1 :(得分:0)

  

有没有更好的方法来构建它?

这里的一个解决方案是使用double dispatch,这是目标函数由两个对象的具体类型决定的地方。在像C#这样的语言中,默认的函数调用机制是单一调度,即目标函数只是由单个对象的具体类型决定。

在C#或C ++等语言中,可以通过visitor pattern实现双重调度。例如,在您的情况下,这可能如下所示:

interface BoundingVolume
{
    bool CollidesWith(BoundingVolume v);

    bool CollidesWith(BoundingBox b);
    bool CollidesWith(BoundingSphere s);
}

class BoundingBox : BoundingVolume
{
    public bool CollidesWith(BoundingVolume v)
    {
        return v.CollidesWith(this);
    }

    public bool CollidesWith(BoundingBox b)
    {
        Console.WriteLine("box/box");
        return true;
    }

    public bool CollidesWith(BoundingSphere s)
    {
        Console.WriteLine("box/sphere");
        return true;
    }
}

class BoundingSphere : BoundingVolume
{
    public bool CollidesWith(BoundingVolume v)
    {
        return v.CollidesWith(this);
    }

    public bool CollidesWith(BoundingBox b)
    {
        Console.WriteLine("sphere/box");
        return true;
    }

    public bool CollidesWith(BoundingSphere s)
    {
        Console.WriteLine("sphere/sphere");
        return true;
    }
}

class Program
{
    static void Main(string[] args)
    {
        BoundingVolume v1 = new BoundingBox();
        BoundingVolume v2 = new BoundingSphere();
        Console.WriteLine(v1.CollidesWith(v2));
    }
}

你可以想象盒子/球体和球体/盒子实现只是调用一个常见的碰撞效用函数来检测球体和盒子之间的碰撞。

  

此外,每次添加新子类时都必须扩展每个子类,从而有效地消除了首先拥有子类的所有好处

根据它们的作用,子类仍然可以通过其他多态行为给你带来好处,例如: ContainsPoint虚拟方法会返回特定BoundingVolume是否包含Point

基本上,如果你需要特殊情况下每种类型的碰撞,你无法避免组合爆炸,因为某些地方需要说“如果我有X和Y,我该如何计算他们的交汇有效吗?“这可能是一个很大的switch语句,一个方法表,或者通过上面提到的访问者模式的双重调度。

答案 2 :(得分:0)

在不知情的情况下,听起来你可能错过了一定程度的抽象。

在一个理想的情况下,会有一个CollissionDetector可以使用足够的信息来检测碰撞。现在,可能,各种对象的形状可能会大不相同,就像你一样指出,但这就是为什么你希望将代码分成不同的类,但没有任何内容,但是重载方法或(甚至更好)。重载会识别像IBoundingBox,IEllipitcalBoundingBox等等,并从那里处理它们。

你说得对:这不是你想让每个盒子都知道的东西。最后,你会想知道哪个盒子知道什么,你会发现你违反了最少知识的原则。