2012-01-15 12 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

+0

@buc問題は、特定の2つのオブジェクトを互いに衝突させたいということです。例えば、s1宇宙船をa1小惑星と衝突させる、s1船をa2小惑星と衝突させるなど。 RTTIで実現するのは非常に簡単ですが、ここでは何か間違っている必要があります。 – Firkraag

+0

この場合、オブジェクトは、他のオブジェクトに何が衝突しているかに応じてアクセプタとビジターの両方として動作する必要があります。これを実現するには、IVisitorインターフェイスを実装する必要があり、特定の訪問者クラスは必要ありません。私は私の答えに例を示します... – buc

答えて

2

は、あなたが別のオブジェクトが相互に衝突することができることを実現したい、そして、そのような衝突が発生した場合、参加者は、彼らがに衝突した物体のどのような他の種類を知っているだろう。

リフレクション(つまり、RTTIはC++用語ですが)を使用せずにこれを達成するには、Visitorパターンを使用するのが良い方法です。このシナリオでは、どちらのオブジェクトがどのオブジェクトに衝突するかに応じてアクセプタとビジターの両方として動作するということを間違っています。我々はに衝突しているオブジェクトは、(「オブジェクトを衝突受け入れる」)、およびは、他の一つにに衝突しているオブジェクトは、訪問者となり(「訪問/アクセプターオブジェクトに衝突した)アクセプタとなります。

アクセル/ビジターの役割は、衝突するオブジェクトが他のもの(逆に、宇宙船に衝突する移動する小惑星対静止した小惑星に衝突する移動する宇宙船)である場合があります。この例から、1つのオブジェクトがアクセプタまたは訪問者これはクラス階層に反映される必要があるため、両方のオブジェクトでICollidableインターフェイスとIVisitorインターフェイスのいずれかを実装する必要があります。

コードを書き換えました投稿したので、AsteroidクラスとSpaceshipクラスの両方が2つのインターフェースを実装しています。私はそれを願っています

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

:追加の訪問者クラスは、私たちのオブジェクトは、その人自身が訪問者になったので、もう必要とされていません。

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

そして、あなたがプログラムを実行する場合は、コンソールで次のような出力が得られますこの種のシナリオでVisitorパターンを使用する方法を明確にしました。

+0

ありがとうございました。それは確かに動作しますが、私は少し複雑な感情を持っています。私は、IVistorインターフェイスを実装しているオブジェクトを見たことがないと思います(明示的なビジターオブジェクトを除く)。あなたの方法は非常に興味深いですが、おそらく、IVisitorを実装するためにSpaceShipとAsteroidクラスを必要としないソリューションがありますか? – Firkraag

+0

Visitorパターンは、通常、コレクションの要素(アクセプタ)を反復し、それらに対してさまざまな操作(訪問者)を実行するために使用されます。その場合、ロールは一定であり、コレクション要素は常に「受動的」であり(常にアクセプタである)、訪問者は常に訪問者である。 ロールが混じって(質問のように)、オブジェクトがアクセプタとしてもビジターとしても動作する必要がある場合、最も簡単な解決策は、両方のインターフェイスを実装することです。 – buc

1

the Mediator patternをご覧ください。

はWikipediaのページ、メディエーターパターンと

によれば、オブジェクト間の通信を媒介オブジェクトにカプセル化されます。オブジェクトはもはや互いに直接通信するのではなく、メディエータを介して通信します。これにより、通信するオブジェクト間の依存関係が軽減され、結合が低下します。

具体的には、メディエータはすべてのCollidableが登録されるクラスであり、それらは衝突のために監視されます。衝突が発生すると、メディエータは両方の衝突するオブジェクトに対してHandleCollision(Collidable other)メソッドを呼び出すことになります。

これに関するもう1つの利点は、具体的な実装に束縛されていないことです。あなたの衝突メカニズムはCollidable抽象に依存します。明日は別のクラスを追加して、Collidableインターフェイスを実装すれば、他のものを変更することなく、このメカニズムに合わせる準備が整います。

+0

衝突するオブジェクトが衝突した他のオブジェクトの種類を知る必要がある場合、小惑星 - 小惑星の衝突により小惑星がさらに生成されますが、小惑星 - 宇宙船の衝突は宇宙船を破壊します。訪問者パターンでは、参加するオブジェクトの実際のタイプがわかります。 – buc

+0

@bucそうです。私は疎結合の考え方に夢中になりました。プログラムはすべての可能なタイプの衝突を記述しなければならないので、この種の疎結合はこの場合達成できないように見えます。 – GolfWolf

関連する問題