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

C# 这种情况下代码结构的最佳实践方法?,c#,design-patterns,C#,Design Patterns,嘿,我有一个关于代码结构的问题,我想知道什么是最佳实践,或者我是否应该使用特定的设计模式 我有一个抽象基类BoundingVolume,它可以有BoundingSphere、BoundingBox等子类。必须在两个BoundingVolume之间进行比较,以确定它们是否重叠,此比较由一个世界级软件进行,该软件跟踪模拟中的所有对象,并负责处理体积之间的碰撞 我最初的想法是,世界级将保存BoundingVolume对象的集合,并使用BoundingVolume上定义的方法调用虚拟碰撞,该方法将接受另

嘿,我有一个关于代码结构的问题,我想知道什么是最佳实践,或者我是否应该使用特定的设计模式

我有一个抽象基类BoundingVolume,它可以有BoundingSphere、BoundingBox等子类。必须在两个BoundingVolume之间进行比较,以确定它们是否重叠,此比较由一个世界级软件进行,该软件跟踪模拟中的所有对象,并负责处理体积之间的碰撞

我最初的想法是,世界级将保存BoundingVolume对象的集合,并使用BoundingVolume上定义的方法调用虚拟碰撞,该方法将接受另一个BoundingVolume进行比较。问题在于确定两个边界体积重叠的算法对于每个子类是否不同,例如,“球体与球体碰撞”的算法与“长方体与长方体碰撞”或“长方体与球体碰撞”的算法不同

使用我的初始计划,每个子类都必须确定传递给它的BoundingVolume的实际类型,并进行切换以使用正确的算法。此外,每次添加新的子类时,每个子类都必须进行扩展,从而有效地消除了拥有子类的所有好处。有没有更好的方法来组织这个

我更多的是寻找一个解决方案,而不是这个特定场景的答案


感谢您的帮助。

在这种情况下,单独的函数似乎是最好的解决方案。创建一个函数,它是子类的朋友,因此它可以访问它的成员。要确定传递给它的子类,它可以读取私有/受保护的成员函数类名

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

我希望我说得有道理。我在iPhone上打这个

有没有更好的方法来组织这个

这里的一个解决方案是使用,即目标函数由两个对象的具体类型决定。在C等语言中,默认的函数调用机制是单分派,即目标函数仅由单个对象的具体类型确定

在C语言或C++语言中,双调度是通过例如,在您的情况下,这可能如下所示:

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));
    }
}
您可以想象,box/sphere和sphere/box实现只是调用一个公共碰撞实用程序函数来检测球体和长方体之间的碰撞

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

根据它们所做的,子类仍然可以通过其他多态行为为您带来好处,例如ContainsPoint虚拟方法将返回特定BoundingVolume是否包含点


但是,从本质上讲,如果你需要对每种类型的碰撞进行特殊处理,你就无法避免组合爆炸,因为有些地方需要说,如果我有一个X和一个Y,我如何有效地计算它们的交集?这可能是一个大的switch语句、一个方法表,或者是上面提到的通过访问者的双重分派模式。

不知道更多,听起来您可能缺少一个抽象级别

在一个理想的场景中,会有一个碰撞检测器,它可以获取具有足够信息的对象来检测碰撞。现在,正如您所指出的,各种对象的形状可能会有很大的不同,但这就是为什么您希望将代码分离到一个不同的类中,其中没有任何内容,只有重载方法,甚至更好。重载将识别像IBoundingBox、IEllipitcalBoundingBox之类的东西,并从那里处理它们


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

每一个问题都可以通过另一个间接层来解决。但是扩展性真的对你那么重要吗?我可以想象一个物理模拟是非常关键的性能,你会喜欢原始速度而不是一个好的可扩展API。你是对的,性能在物理模拟中可能更重要。然而,由于我只是在自己的娱乐时间里做这个项目,表演对我来说并不是什么大问题。我计划随着时间的推移添加到项目中,从简单开始,并随着时间的推移添加更多的功能,因此我希望以后能够相对容易地进行扩展。信息技术
当我对整个功能集感到满意时,这种情况可能会进一步出现,性能变得更加重要,我必须为此进行重组。我非常喜欢这种方法,它肯定解决了我最初尝试时遇到的问题。感谢您花时间提供代码示例。我将把这个标记为答案,尽管我意识到这个问题有点主观。我想我会关注你的观点,但问题是如何得到正确的重载调用?世界级只知道每个IBoundingBox和IEllipticalBoundingBox的基类,因此在某个时候,必须有一种方法将其转换为正确的子类型,以便调用其重载。啊,但这就是它的美妙之处。抽象基类不知道。具体类型确实如此,它们提供了IBoundingBox或IEllipticalBoundingBox甚至I3DBoundingBox的显式实现!那是你的碰撞探测器用的。关键是,抽象基类永远不应该知道它的派生。衍生品可以知道它们的祖先tho,协调人也可以知道。但是协调员应该尽可能松散地结合起来。进一步考虑:在你的理论游戏世界中考虑一个移动车辆类。它将派生自MobileObject基类,该基类仅具有标记接口IMobileObject。然后,MovingVehicle提供了I3DBoundingBox的具体实现。CollisionDetector提供了一种方法,即CollisionOccurredBoundingBox mob1、IBoundingBox mob2。现在,IBoundingBox接口可以作为IEllipticalBoundingBox和I3DBoundingBox接口的基类,简化您的生活。我明白您的说法,它肯定会起作用。稍后从性能角度将此方法与Chris Schmich给出的方法进行比较可能会很有趣。我必须承认,引入一个成员字段来标识每个子类的想法确实让我感到有些混乱,但这可能是我没有透过树看到森林的情况。代码将不可避免地感到混乱,充满了变通方法,等等。。。。尤其是当你像我一样是完美主义者时: