2012-01-15 71 views
3

我尝试了解双重调度和访问者模式,但下面的代码显然是错误的。我必须错过明显的东西,但我不知道如何解决它。任何人都可以照亮我吗?我不知道如何在访问者中添加具体的实例,我应该在具体的访问者类中创建一个构造函数吗?访客模式的实现。小行星/太空飞船碰撞问题

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 
    } 
+0

这段代码为什么会出错?你有没有试过编译它?在C#中,如果不指定构造函数,编译器将为您定义一个空的默认构造函数,并且您标记的行完全合法。据我所见,此代码的工作原理和行为如预期。 – buc 2012-01-15 13:12:29

+0

@buc问题是我想让特定的两个对象相互碰撞。例如使s1飞船与a1小行星相撞,或者s1飞船与a2小行星相撞等等。用RTTI很容易实现,但在这里我必须做错事。 – Firkraag 2012-01-15 13:34:40

+0

在这种情况下,您的对象必须表现为接受者和访问者,具体取决于碰撞到另一个对象的内容。为了实现这一点,他们都必须实现IVisitor接口,并且不需要特定的访问者类。我会在我的答案中显示一个例子... – buc 2012-01-15 13:44:18

回答

2

至于我能理解,你想达到的是,不同的对象可能碰撞到对方,当这种冲突发生的与会者将了解其他类型的对象,他们已经向相撞。

为了在不使用反射(或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 

我希望它为您明确了如何在这种情况下使用访问者模式。

+0

非常感谢您的解决方案。它确实有效,但我有点复杂的感觉。我想我从未见过实现IVistor接口的对象(除了明确的访客对象)。你的方法非常有趣,但也许有一种解决方案不需要SpaceShip和Asteroid类来实现IVisitor? – Firkraag 2012-01-15 15:28:58

+0

Visitor模式通常用于迭代集合的元素(接受者)并对它们执行各种操作(访问者)。在这种情况下,角色是不变的,收集元素总是“被动的”(他们总是接受者),而访客总是访客。 当角色混淆(就像他们在你的问题中那样)并且对象必须表现为接受者和访问者,最简单的解决方案是让它实现两个接口。 – buc 2012-01-15 15:48:20

1

你不妨看看the Mediator pattern

根据Wikipedia页面,

随着介体图案,对象之间的通信中封装有介体对象。对象不再直接相互通信,而是通过中介进行通信。这减少了通信对象之间的依赖关系,从而降低了耦合度。

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

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

+0

如果碰撞物体必须知道它们碰撞的其他物体(小行星 - 小行星碰撞会产生更多的小行星,但是小行星 - 宇宙飞船碰撞会破坏太空船),这并不能解决问题。通过访问者模式,参与对象的实际类型是已知的。 – buc 2012-01-15 16:28:44

+0

@buc你说得对。我被松散耦合的想法带走了。看起来在这种情况下无法实现这种松耦合,因为程序必须描述所有可能的碰撞类型。 – GolfWolf 2012-01-15 16:53:17