访客模式的实现。小行星/太空飞船碰撞问题

时间:2012-01-15 12:47:36

标签: c# design-patterns visitor

我尝试了解双重调度和访问者模式,但以下代码显然是错误的。我必须遗漏一些明显的东西,但我不知道如何解决它。任何人都可以照亮我吗?我不知道如何在访问者中添加具体实例,我应该在具体的访问者类中创建构造函数吗?

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
    }

2 个答案:

答案 0 :(得分:2)

据我所知,你想要实现不同的物体可以相互碰撞,当发生这种碰撞时,参与者会知道他们碰撞的其他物体是什么。

为了实现这一目标而不使用反射(或者如你所说的RTTI,虽然它是一个C ++术语),这是使用访问者模式的好方法。你做错了,在这种情况下,两个对象都将作为接受者和访问者,取决于与哪个对象发生冲突。我们碰撞到的对象将是接受者(“接受碰撞对象”),而正在碰撞的对象成为访问者(“访问/碰撞”到受体对象)。

当碰撞物体是另一个时,受体 - 参观者角色可能会被逆转(移动的小行星碰撞到宇宙飞船而不是移动的宇宙飞船碰撞到静止的小行星)。从这个例子可以看出,根据具体情况,一个对象可以充当接受者或访客。这必须反映在类层次结构中,因此两个对象都必须实现ICollidable和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

我希望您明确了如何将访客模式用于此类情景。

答案 1 :(得分:1)

您还可以查看the Mediator pattern

根据维基百科页面,

  

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

具体而言,在您的情况下,Mediator将是一个所有Collidable s将被注册的类,并将监视它们是否存在冲突。发生冲突时,Mediator会在两个碰撞对象上调用HandleCollision(Collidable other)方法。

这方面的另一个优点是你不受任何具体实施的束缚;你的碰撞机制取决于Collidable抽象。明天你可以添加另一个类,只需要实现Collidable接口,它就可以适应这种机制,而不需要改变其他任何东西。