c# - 如何为其派生类型的每种可能组合实现基类的方法

标签 c# oop inheritance

我有以下由多个其他类实现的 Shape 接口(interface),例如 Rectangle、Circle、Triangle ...

interface IShape{
    bool IsColliding(IShape other);
}

IsColliding 方法应该检查一个 Shape 是否与另一个 Shape 发生碰撞,而不管它们的具体类型。 然而,每一对形状(矩形/矩形、矩形/圆形、圆形/三角形等)都有自己的碰撞检查实现。

我正在努力为这个问题找到一个好的设计解决方案。

天真的方法是切换“其他”形状的类型以调用正确的实现:

class Rectangle : IShape{
    bool IsColliding(IShape other){
        if(other is Rectangle){
            return CollisionHandler.CheckRectangleVsRectangle(this,(Rectangle)other);
        }else if(other is Circle){
            return CollisionHandler.CheckRectangleVsCircle(this,(Circle)other);
        } else
            // etc ...
    }
}

但添加新形状意味着修改每个派生类中的方法以添加新案例。

我还想像这样调用一个独特的静态方法:

static bool IsColliding(IShape shapeA, IShape shapeB);

但即使它集中了所有内容,它也会使要执行的类型测试的数量加倍,而且我仍然必须在每个第一级“if”中添加一个新案例。

if(shapeA is Rectangle){
    if(shapeB is Rectangle){
        // Rectangle VS Rectangle
    }else if(shapeB is Circle){
        // Rectangle VS Circle
    }else{
        // etc ...
    }
}else if(shapeA is Circle){
    if(shapeB is Rectangle){
        // Rectangle VS Circle
    }else{
        // etc ...
    }
} // etc ...

那么,如何才能更好地设计它呢?

最佳答案

这里有一个使用double dispatch的想法(超越访问者模式的原理):

基本事实是碰撞函数是对称的。 IE。 IsCollision(shapeA, shapeB) = IsCollision(shapeB, shapeA)。因此,您不需要实现每个 n^2 组合(n 是形状类的数量),但只需实现其中的大约一半:

         circle  tri rect
circle      x     x    x
tri               x    x
rec                    x

所以假设你有一个形状的顺序,每个形状都会与位于它们之前或相等的形状发生碰撞。

在此实现中,形状特定的碰撞处理被分派(dispatch)给一个名为 CollisionHandler 的对象。以下是界面(为简洁起见进行了简化):

interface IShape
{
    int CollisionPrecedence { get; }
    AbstractCollisionHandler CollisionHandler { get; }
    void Collide(AbstractCollisionHandler handler);
}

class AbstractCollisionHandler
{
    public virtual void Collides(Circle other) { throw new NotImplementedException(); }
    public virtual void Collides(Rect other) { throw new NotImplementedException(); }
}

基于这些接口(interface),具体的形状类有:

class CircleCollisionHandler : AbstractCollisionHandler
{
    public override void Collides(Circle other)
    {
        Console.WriteLine("Collision circle-circle");
    }
}
class Circle : IShape
{
    public int CollisionPrecedence { get { return 0; } }
    public AbstractCollisionHandler CollisionHandler { get { return new CircleCollisionHandler(); } }
    public void Collide(AbstractCollisionHandler handler) { handler.Collides(this); }
}

class TriCollisionHandler : AbstractCollisionHandler
{
    public override void Collides(Circle other)
    {
        Console.WriteLine("Collision tri-circle");
    }

    public override void Collides(Tri other)
    {
        Console.WriteLine("Collision tri-tri");
    }
}

class Tri : IShape
{
    public int CollisionPrecedence { get { return 1; } }
    public AbstractCollisionHandler CollisionHandler { get { return new TriCollisionHandler(); } }
    public void Collide(AbstractCollisionHandler handler) { handler.Collides(this); }
}

调用具体碰撞函数的函数是:

static void Collides(IShape a, IShape b)
{
    if (a.CollisionPrecedence >= b.CollisionPrecedence)
        b.Collide(a.CollisionHandler);
    else
        a.Collide(b.CollisionHandler);
}

如果你现在想实现另一个形状 Rect,那么你必须做三件事:

改变 AbstractCollisionHandler 以包含矩形

abstract class AbstractCollisionHandler
{
    ...
    public virtual void Collides(Rect other) { throw new NotImplementedException(); }
}

实现碰撞处理器

class RectCollisionHandler : AbstractCollisionHandler
{
    public override void Collides(Circle other)
    {
        Console.WriteLine("Collision rect-circle");
    }

    public override void Collides(Tri other)
    {
        Console.WriteLine("Collision rect-tri");
    }

    public override void Collides(Rect other)
    {
        Console.WriteLine("Collision rect-rect");
    }
}

并在Rect类中实现相关接口(interface)方法:

class Rect : IShape
{
    public int CollisionPrecedence { get { return 2; } }
    public AbstractCollisionHandler CollisionHandler { get { return new RectCollisionHandler(); } }
    public void Collide(AbstractCollisionHandler handler) { handler.Collides(this); }

}

就这么简单。这是一个显示调用函数的小测试程序:

Collides(new Circle(), new Tri());
Collides(new Tri(), new Circle());
Collides(new Rect(), new Circle());

输出:

Collision tri-circle
Collision tri-circle
Collision rect-circle

关于c# - 如何为其派生类型的每种可能组合实现基类的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39317887/

相关文章:

c++ - override函数什么时候调用上层函数?在其他代码之前还是之后?

java - 如何在不进行强制转换的情况下重写时指定方法参数?

c# - 一些常见的 WMI 查询是什么

c# - 将默认值设置为选择列表

c# - 在 C# 中将字节数组转换为短数组

PHP:如何自动包含和实例化类?

java - Java中的子类和父类(super class)

python - OOP如何让脚本(游戏)运行

Maven:多个父 POM?

c# - Automapper 中属性的严格类型验证