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

发布于 2024-12-27 05:53:05 字数 1505 浏览 3 评论 0原文

我尝试了解双重调度和访问者模式,但以下代码显然是错误的。我一定错过了一些明显的东西,但我不知道如何修复它。谁能照亮我吗?我不知道如何在访问者中放置具体实例,我应该在具体访问者类中创建一个构造函数吗?

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
    }

I try to learn about double dispatch and the visitor pattern, but the following code is clearly wrong. I must be missing something obvious but I don't know how to fix it. Can anyone illuminate me? I don't know how to put concrete instances in the visitor, should I create a constructor in concrete visitor classes?

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
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

仙气飘飘 2025-01-03 05:53:05

据我所知,您希望实现不同的物体可以相互碰撞,并且当发生这种碰撞时,参与者会知道他们碰撞到了哪种其他类型的物体。

要在不使用反射(或您所说的 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

我希望它已经说清楚了。为您介绍如何使用此类场景的访客模式。

As I can understand, you want to achieve that the different objects could collide to each other, and when such a collision occurs the participants would know what other kind of object they had collided to.

To achieve this without using reflection (or RTTI as you say, although it's a C++ term), it's good approach to use the Visitor pattern. What you have done wrong it that in this scenario both object would act as acceptor and visitor depending on which is collided to which. The object we are colliding to will be the acceptor ("accepts colliding object"), and the object that is colliding into the other one becomes the visitor ("visits/collides to the acceptor object).

The acceptor-visitor roles may be reversed when the colliding object is the other one (moving asteroid colliding into a spaceship vs. moving spaceship colliding into a stationary asteroid). From this example you can see, that one object may act as acceptor or visitor depending on the case. This must be reflected in the class hierarchy, so both objects have to implement either the ICollidable and the IVisitor interface.

I rewrote the code you have posted, so both Asteroid and Spaceship classes implement the two interfaces. The additional visitor classes are not needed anymore, because our objects themself became the visitor:

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);
    }
}

And if you run the program you will get the following output in the console:

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

I hope it made clear for you how to use the Visitor pattern for this kind of scenarios.

浮生未歇 2025-01-03 05:53:05

您不妨看看中介者模式

根据维基百科页面,

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

具体来说,在您的情况下,Mediator 将是一个类,所有 Collidable 都将注册到该类,并监视它们的冲突。当发生碰撞时,Mediator 将对两个碰撞对象调用 HandleCollision(Collidable other) 方法。

这样做的另一个优点是您不受任何具体实现的约束;您的碰撞机制取决于 Collidable 抽象。明天您可能会添加另一个类,只需实现 Collidable 接口,它就可以适应此机制,而无需更改其他任何内容。

You might as well take a look at the Mediator pattern.

According to the Wikipedia page,

With the mediator pattern, communication between objects is encapsulated with a mediator object. Objects no longer communicate directly with each other, but instead communicate through the mediator. This reduces the dependencies between communicating objects, thereby lowering the coupling.

Concretely, in your case the Mediator would be a class to which all the Collidables would be registered and that will monitor them for collisions. When a collision occurs, the Mediator would call a HandleCollision(Collidable other) method on both colliding objects.

Another advantage about this is that you are not bound to any concrete implementations; your collision mechanism depends on the Collidable abstraction. Tomorrow you may add another class and just by making if implement the Collidable interface it'll be ready to fit in this mechanism, without changing anything else.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文