21

IDataIOというインターフェイスがあります。

public interface IDataIO
{
  event DataReceivedEvent DataReceived;
  //.....more events,methods and properties
}

このインターフェイスを実装する複数のクラス、つまり、、もUdpIOありTcpIOますSerialIO

これで、IO異なる入出力ハードウェアを切り替えることができるクラスができました。このクラスの各インスタンスには、、、またはのいずれかのプロパティがCurrentIODeviceあります。このプロパティが割り当てられると、1つ以上のハンドラーをアタッチして、受信データが受信されたときにGUIに通知されるようにします。また、通知が必要な他のクラスも通知します。SerialIOUdpIOTcpIODataReceivedEvent

public class IO
{
  IDataIO CurrentIODevice;

  public IO()
  {
    SerialIO serial = new SerialIO();
    TcpIO tcp = new TcpIO();
    UdpIO udp = new UdpIO();
    CurrentIODevice = serial;
  }
}

複数のオブジェクトIOManagerを保持するクラスもあります。IO

public class IOManager
{
  List<IO> Ports = new List<IO>();
  public IOManager()
  {
    Ports.Add(new IO());
    Ports.Add(new IO());
  }

  Ports[0].CurrentIODevice = serial;
  Ports[0].CurrentIODevice.DataReceivedHandler += MyGuiUpdate;
  Ports[0].CurrentIODevice.DataReceivedHandler += MyDataProcessing;
}

私の懸念(問題のATMではありません)は、実行時に異なるIDataIOインターフェイス間でどのように変更するかです。

実行時に次のステートメントを実行すると、どのような影響がありますか。

//i know this is illegal but just to demonstrate
IOManager.Ports[0].CurrentIODevice = tcp; 

イベントハンドラーは引き続き機能しますか(そして正しく機能しますか)?

CurrentIODeviceが割り当てられる前にイベントの割り当てを解除し、その後でハンドラーを再度割り当てる必要がありますか?これが事実である場合、私はこのアプローチがかなり厄介になっているのを見ることができるので、誰かがこの問題に対してより良いアプローチを持っているなら、私はすべての耳です:)

4

2 に答える 2

18

いいえ、ハンドラーは古いオブジェクトにアタッチされているため、機能しません。インターフェイスは...オブジェクトへのインターフェイスを提供します。これを一種のコントラクトと見なしますが、それ自体は別のオブジェクトではありません。

インターフェイスのさまざまな実装を(実行時に)切り替えて、すべてのハンドラーを機能させ続ける必要がある場合は、インターフェイス自体に対して同じオブジェクト参照、一種の戦略パターン(多かれ少なかれ)が必要です。

あなたの場合、例えば、オブジェクトにIDataIOインターフェースを実装することができDataIOます。プロパティ(またはメソッド、その意図はより明確だと思います)を公開して、そのインターフェイスのさまざまな実装(シリアル、TCPなど)を切り替えます。イベントハンドラーをそのインターフェイスにアタッチする唯一のオブジェクトになります(具体的な実装が変更されると、ハンドラーが削除されます)。そのオブジェクトのユーザーは、それが使用している具体的な実装が何であれ、常にそれを見るでしょう。

これは、この概念を説明するための小さな例です。一般的なインターフェースは次のとおりです。

interface IDataIO
{
    void Write(byte[] data);

    byte[] Read();

    event EventHandler DataReceived;
}

これはIDataIOの具体的な実装であり、他のクラスはこのクラスのみを直接使用します。

sealed class DataIO : IDataIO
{
    public void SetChannel(IDataIO concreteChannel)
    {
        if (_concreteChannel != null)
            _concreteChannel.DataReceived -= OnDataReceived;

        _concreteChannel = concreteChannel;
        _concreteChannel.DataReceived += OnDataReceived;
    }

    public void Write(byte[] data)
    {
        _concreteChannel.Write(data);
    }

    public byte[] Read()
    {
        return _concreteChannel.Read();
    }

    public event EventHandler DataReceived;

    private IDataIO _concreteChannel;

    private void OnDataReceived(object sender, EventArgs e)
    {
        EventHandler dataReceived = DataReceived;
        if (dataReceived != null)
            dataReceived(this, e);
    }
}

最後に、テスト用のコード:

class Test
{
    public Test()
    {
        _channel = new TcpIO();

        _channel.DataReceived += OnDataReceived;
    }

    public void SetChannel(IDataIO channel)
    {
        _channel.SetChannel(channel);

        // Nothing will change for this "user" of DataIO
        // but now the channel used for transport will be
        // the one defined here
    }

    private void OnDataReceived(object sender, EventArgs e)
    {
        // You can use this
        byte[] data = ((IDataIO)sender).Read();

        // Or this, the sender is always the concrete
        // implementation that abstracts the strategy in use
        data = _channel.Read();
    }

    private DataIO _channel;
}
于 2012-05-16T08:16:22.367 に答える
3

明らかに、戦略パターンを考慮する必要があります。最初にコードを投稿し、後で説明します。

public interface IDataIO
{
    event DataReceivedEvent DataReceived;

    //this the new added method that each IO type should implement.
    void SetStrategy();
}

public class SerialIO : IDataIO
{
    public void SetStrategy()
    {
        //put the code that related to the Serial IO.
        this.DataReceivedHandler += MyGuiUpdate;
        this.DataReceivedHandler += MyDataProcessing;
    }
}

public class TcpIO : IDataIO
{
    public void SetStrategy()
    {
        //put the code that related to the Tcp IO.
        //I will not implement it because it is a demo.
    }
}

public class UdpIO : IDataIO
{
    public void SetStrategy()
    {
        //put the code that related to the Udp IO.
        //I will not implement it because it is a demo.
    }
}

public class IO
{
    IDataIO port = new IDataIO();

    public void SetIOType(IDataIO ioType)
    {
        this.port = ioType;

        port.SetStrategy();
    }

}

public class IOManager
{
    List<IO> ports = new List<IO>();

    SerialIO serial = new SerialIO();
    TcpIO tcp = new TcpIO();

    ports[0].SetIOType(serial);
    ports[1].SetIOType(tcp);
}
  1. インターフェイスIDataIOは、すべてのIOタイプが実装する必要がある基本を定義します。

  2. IDataIOから派生したSerialIO、TcpIO、UdpIOクラスは、それぞれのニーズを満たすためにメソッドSetStrategy()を実装します。

  3. IOクラスは、IDataIOタイプを参照するフィールド(名前付きポート)を所有します。このフィールドは、IOクラスで定義されたメソッドSetIOType()を呼び出すことにより、実行時に特定のIOタイプに設定できます。このメソッドが呼び出されると、「port」フィールドがどのタイプを参照しているかがわかり、SetStrategy()メソッドを呼び出すと、IOクラスの1つでオーバーライドされたメソッドが実行されます。

  4. IOManagerクラスはクライアントです。特定のIOタイプ(SerialIOなど)が必要な場合は、IOクラスを新規作成し、SerialIOクラスインスタンスを渡してSetIOType()メソッドを呼び出すだけで、SerialIOタイプに関連するすべてのロジックが自動的に設定されます。

私の説明がお役に立てば幸いです。

于 2012-05-16T09:39:34.080 に答える