访问者模式的实现.小行星/太空船碰撞问题

本文关键字:太空船 碰撞 问题 小行星 模式 实现 访问者 | 更新日期: 2023-09-27 18:20:15

我试图了解双重调度和访问者模式,但下面的代码显然是错误的。我一定错过了一些显而易见的东西,但我不知道如何解决。有人能告诉我吗?我不知道如何在访问者中放置具体的实例,我应该在具体的访问者类中创建一个构造函数吗?

interface Collidable
{
    void Accept(IVisitor other);
}
class Asteroid : Collidable
{
    public void Accept(IVisitor other)
    {
        Console.Write("[Asteroid] ");
        other.visitAsteroid(this);
    }
}
class Spaceship : Collidable
{
    public void Accept(IVisitor other)
    {
        Console.Write("[Spaceship] ");
        other.visitSpaceship(this);
    }
}
interface IVisitor
{
    void visitAsteroid(Asteroid a);
    void visitSpaceship(Spaceship s);
}
class CollisionWithAsteroidVisitor : IVisitor
{
    public void visitAsteroid(Asteroid a)
    {
        Console.WriteLine("Collided with asteroid");
    }
    public void visitSpaceship(Spaceship s)
    {
        Console.WriteLine("Collided with asteroid");
    }
}
class CollisionWithSpaceShipVisitor : IVisitor
{
    public void visitAsteroid(Asteroid a)
    {
        Console.WriteLine("Collided with spaceship");
    }
    public void visitSpaceship(Spaceship s)
    {
        Console.WriteLine("Collided with spaceship");
    }
}
    static void Main(string[] args)
    {
        Asteroid a1 = new Asteroid();
        Asteroid a2 = new Asteroid();
        Spaceship s1 = new Spaceship();
        Spaceship s2 = new Spaceship();
        s1.Accept(new CollisionWithAsteroidVisitor()); // this must be wrong
        s1.Accept(new CollisionWithSpaceShipVisitor()); // this must be wrong
    }

访问者模式的实现.小行星/太空船碰撞问题

正如我所理解的,你想实现不同的物体可以相互碰撞,当这种碰撞发生时,参与者会知道他们碰撞到了什么其他类型的物体。

为了在不使用反射(或者RTTI,尽管它是C++术语)的情况下实现这一点,使用Visitor模式是一种很好的方法。你做错了什么,在这种情况下,两个对象都将充当接受者和访问者,这取决于哪个对象与哪个对象碰撞。我们碰撞到的对象将是受体("接受碰撞对象"),与另一个相撞的对象将成为访问者("访问/碰撞到受体对象")

当碰撞物体是另一个时,受体-访问者的角色可能会颠倒(移动的小行星碰撞到宇宙飞船与移动的宇宙飞船碰撞到静止的小行星)。从这个例子中,您可以看到,根据具体情况,一个对象可能充当接受者或访问者。这必须反映在类层次结构中,因此两个对象都必须实现ICollible和IVisitor接口。

我重写了您发布的代码,所以Asteroid和Spaceship类都实现了这两个接口。不再需要额外的访问者类,因为我们的对象本身就是访问者:

interface ICollidable
{
    void Accept(IVisitor other);
}
interface IVisitor
{
    void VisitAsteroid(Asteroid a);
    void VisitSpaceship(Spaceship s);
}
class Asteroid : ICollidable, IVisitor
{
    public void Accept(IVisitor other)
    {
        Console.Write("[Asteroid] ");
        other.VisitAsteroid(this);
    }
    public void VisitAsteroid(Asteroid a)
    {
        Console.WriteLine("Collided with asteroid");
    }
    public void VisitSpaceship(Spaceship s)
    {
        Console.WriteLine("Collided with asteroid");
    }
}
class Spaceship : ICollidable, IVisitor
{
    public void Accept(IVisitor other)
    {
        Console.Write("[Spaceship] ");
        other.VisitSpaceship(this);
    }
    public void VisitAsteroid(Asteroid a)
    {
        Console.WriteLine("Collided with spaceship");
    }
    public void VisitSpaceship(Spaceship s)
    {
        Console.WriteLine("Collided with spaceship");
    }
}

class Main
{
    public static void Main(string[] args)
    {
        Asteroid a1 = new Asteroid();
        Asteroid a2 = new Asteroid();
        Spaceship s1 = new Spaceship();
        Spaceship s2 = new Spaceship();
        s1.Accept(a1);
        s1.Accept(as);
        a1.Accept(s1);
        a2.Accept(a2);
    }
}

如果你运行这个程序,你会在控制台中得到以下输出:

[Spaceship] Collided with asteroid
[Spaceship] Collided with spaceship
[Asteroid] Collided with spaceship
[Asteroid] Collided with asteroid

我希望它能让你清楚地知道如何在这种场景中使用访问者模式。

您不妨看看Mediator模式。

根据维基百科页面,

使用中介模式,对象之间的通信被一个中介对象封装。对象之间不再直接通信,而是通过中介进行通信。这减少了通信对象之间的依赖关系,从而降低了耦合。

具体地说,在您的案例中,Mediator将是一个所有Collidable都将注册到的类,它将监视它们是否发生冲突。当发生冲突时,Mediator将在两个冲突对象上调用HandleCollision(Collidable other)方法。

这方面的另一个优点是,您不受任何具体实现的约束;您的冲突机制取决于Collidable抽象。明天,您可以添加另一个类,只需使if实现Collidable接口,它就可以适应这个机制,而不需要更改任何其他内容。