2

問題

私は、脳にドキドキするたびに、仮想呼び出しとタイプチェックを使用する必要があることを誓います(例:

if (obj is Foo)
   ...
else if (obj is Bar)
   ...

...前者の実装方法がわからない別の例を思いつきます。

シリアルポートを介してパケット化されたプロトコルを実装しています。いくつかの擬似コードはこれを最もよく説明します:

OnDataReceived:
    RcvPacket p = RcvPacket.ReadPacket(comport);   // Call factory method
    if (p is RcvPacketFoo)
        OnFoo();
    if (p is RcvPacketBar)
        OnBar();

OnFoo:
    raise Foo event
OnBar:
    raise Bar event

基本的に、ReadPacketは、受信するパケットのタイプを判別し、バッファーを正しい派生型コンストラクターに渡す基本クラスのファクトリメソッドです。この後、パケットの種類に応じて、イベントを発生させる必要があります。演算子を使用せずにこれを行うにはどうすればよいですか?私の方法は健全/正気ですか?is


解決

もちろん、ビジターパターン!ありがとうパブロロメオ

この場合、ファクトリメソッドを呼び出すコントローラーをビジターにしました。私の結果:

public interface IPacketHandler {
    void Handle(FooPacket p);
    void Handle(BarPacket p);
}

public class Controller : IPacketHandler {
    OnDataReceived() {
        RcvPacket p = RcvPacket.ReadPacket(comport);   // Call factory method
        p.Handle(this);        // *** Virtual Call: The first dispatch ***
    }

    // Note the use of explicit interface implementation here.
    IPacketHandler.Handle(FooPacket p) {
       OnFoo();
    }
    IPacketHandler.Handle(BarPacket p) {
       OnBar();
    }
}

public abstract class RcvPacket {
    public static RcvPacket ReadPacket(...) { ... }   // Factory method
    public void Handle(IPacketHandler handler);
}
public class FooPacket : RcvPacket {
    public override void Handle(IPacketHandler handler) {
       handler.Handle(this);        // *** Overloaded call: The second dispatch ***
    }
}
public class BarPacket : RcvPacket {
    public override void Handle(IPacketHandler handler) {
       handler.Handle(this);        // *** Overloaded call: The second dispatch ***
    }
}

ここでの楽しい点は、ビジターインターフェイスを明示的に実装することにより、Handle呼び出しが本質的に非表示になることです。MSDNから:

インターフェイスを実装するクラスは、そのインターフェイスのメンバーを明示的に実装できます。メンバーが明示的に実装されている場合、クラスインスタンスを介してアクセスすることはできず、インターフェイスのインスタンスを介してのみアクセスできます。

4

2 に答える 2

4

私が考えることができる唯一の方法は、andの実装をandクラスOnFooに移動することです。OnBarRcvPacketFooRcvPacketBar

public class RcvPacket{
     public abstract void On(RcvPacketHandler eh);
}
public class RcvPacketFoo : RcvPacket
{
     public override void On(RcvPacketHandler eh){eh.OnFoo();} //OnFoo implemenation
}

public class RcvPacketBar : RcvPacket
{
     public override void On(RcvPacketHandler eh){eh.OnBar();} //OnBar implemenation
}
//Update following your comment:
public class RcvPacketHandler
{
public void OnFoo(){}
public void OnBar(){}
//....
OnDataReceived:
    RcvPacket p = RcvPacket.ReadPacket(comport);   // Call factory method
    p.On(this);
于 2012-05-24T23:01:06.613 に答える
2

おそらく、各パケット内に多くの追加ロジックが必要ないことを考えると、DoubleDispatchを使用してそれを実現できます

その場合、私はおそらく次のようなインターフェースを作成します。

public interface IPacketEvents 
{
    void On(Foo foo);
    void On(Bar bar);
}

すべてのパケットの基本クラスがあると想定しています。その中で私は宣言します:

public abstract void RaiseUsing(IPacketEvents events);

また、パッケージの各サブクラスは、以下を実装します。

public override void RaiseUsing(IPacketEvents events) 
{
    events.On(this);
}

新しいクラスに実装させるIPacketEventsか、ファクトリを呼び出した場所から同じクラスに実装させることができます。2番目のケースでは、通話は次のようになります。

OnDataReceived:
    RcvPacket p = RcvPacket.ReadPacket(comport);   // Call factory method
    p.RaiseUsing(this);

このタイプのディスパッチを使用すると、どのタイプを呼び出すかを「認識」しているため、各タイプが対応するメソッドを呼び出すことになります。すべてのメソッドに同じ「オン」名を使用したことは少し混乱するかもしれませんが、それは完全に必要というわけではありません。それらはOnFoo()とOnBar()である可能性があります。

このタイプの動作は、Visitorパターンでも使用されます。

于 2012-05-24T23:15:29.963 に答える