Реализация шаблона посетителя.Проблема столкновения астероида и космического корабля - PullRequest
3 голосов
/ 15 января 2012

Я пытаюсь узнать о двойной рассылке и шаблоне посетителя, но следующий код явно неверен.Я, должно быть, упускаю что-то очевидное, но я не знаю, как это исправить.Кто-нибудь может осветить меня?Я не знаю, как поместить конкретные экземпляры в посетителя, нужно ли создавать конструктор в конкретных классах посетителей?

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 ]

2 голосов
/ 15 января 2012

Как я понимаю, вы хотите добиться того, чтобы различные объекты могли сталкиваться друг с другом, и когда такое столкновение происходит, участники будут знать, с каким другим видом объектов они столкнулись.

Чтобы достичь этого, не используя рефлексию (или, как вы говорите, RTTI, хотя это термин C ++), рекомендуется использовать шаблон Visitor. Что вы сделали неправильно, так это то, что в этом сценарии оба объекта будут действовать как акцептор и как посетитель, в зависимости от того, с кем столкнулся, с каким. Объект, с которым мы сталкиваемся с , будет являться акцептором («принимает сталкивающийся объект»), а объект, с которым сталкивается с другим, становится посетителем («посещает / сталкивается с объект-акцептор).

Роли акцептора и посетителя могут быть изменены на противоположные, когда сталкивающийся объект является другим (движущийся астероид сталкивается с космическим кораблем или движущийся космический корабль сталкивается с неподвижным астероидом). Из этого примера вы можете видеть, что один объект может выступать в роли акцептора или посетителя в зависимости от случая. Это должно быть отражено в иерархии классов, поэтому оба объекта должны реализовывать интерфейс 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

Надеюсь, вам стало ясно, как использовать шаблон Visitor для такого рода сценариев.

1 голос
/ 15 января 2012

Вы могли бы также взглянуть на образец Посредника .

Согласно странице Википедии,

При использовании шаблона-посредника связь между объектами инкапсулируется с объектом-посредником. Объекты больше не общаются напрямую друг с другом, а вместо этого общаются через посредника. Это уменьшает зависимости между взаимодействующими объектами, тем самым снижая связь.

Конкретно, в вашем случае Mediator будет классом, в который будут зарегистрированы все Collidable, и который будет отслеживать их на наличие коллизий. Когда происходит столкновение, Медиатор вызывает метод HandleCollision(Collidable other) для обоих сталкивающихся объектов.

Еще одним преимуществом этого является то, что вы не привязаны к каким-либо конкретным реализациям; ваш механизм столкновения зависит от Collidable абстракции. Завтра вы можете добавить другой класс, и просто сделав, если реализует интерфейс Collidable, он будет готов вписаться в этот механизм, ничего не меняя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...