IDataIOと呼ばれるインターフェイスがあります。
public interface IDataIO
{
event DataReceivedEvent DataReceived;
//.....more events,methods and properties
}
このインターフェイスを実装する複数のクラス、つまりUdpIO
、TcpIO
、SerialIO
もあります。
これで、異なる入力/出力ハードウェアを切り替えることができるIO
クラスができました。このクラスの各インスタンスにはCurrentIODevice
プロパティがあり、これはSerialIO
、UdpIO
またはTcpIO
のいずれかになります。このプロパティが割り当てられると、1つ以上のハンドラーをDataReceivedEvent
にアタッチして、受信データを受信したときにGUIに通知する必要があるほか、通知が必要な他のクラスを通知します。
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が割り当てられる前にイベントの割り当てを解除し、その後にハンドラーを再度割り当てる必要がありますか?これが事実である場合、私はこのアプローチがかなり厄介になるのを見ることができます、それで誰かがこの問題に対してより良いアプローチを持っているなら、私はすべて耳にしています:)
いいえ、ハンドラーは古いオブジェクトにアタッチされているため、機能しません。インターフェイスは、オブジェクトへのインターフェイスを提供します。それを一種のcontractと見なしますが、それ自体は別のオブジェクトではありません。
インターフェースの異なる実装を(実行時に)切り替える必要があり、すべてのハンドラーを機能させ続けるには、インターフェース自体に同じオブジェクト参照が必要です 戦略パターン (詳細またはもっと少なく)。
あなたの場合、例えば、IDataIO
オブジェクトにDataIO
インターフェースを実装できます。プロパティ(またはメソッド、その意図はより明確だと思います)を公開して、そのインターフェイスの異なる実装(シリアル、TCPなど)を切り替えます。それが唯一のものになります。イベントハンドラーをそのインターフェイスにアタッチする1つのオブジェクト(具体的な実装が変更されると、ハンドラーが削除されます)そのオブジェクトのユーザーは、使用している具体的な実装が何であれ、常にそれを見ることができます。
これは、この概念を説明する小さな例です。一般的なインターフェースはこれです:
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;
}
明らかに、あなたは 戦略パターン を考慮すべきです。最初にコードを投稿し、後で説明します。
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);
}
インターフェイスIDataIOは、すべてのIOタイプが実装する必要がある基本を定義します。
IDataIOから派生したSerialIO、TcpIO、UdpIOクラスは、それぞれ独自のニーズを満たすためにメソッドSetStrategy()を実装します。
IOクラスはフィールドを所有します(名前付きポート)はIDataIOタイプを参照します。このフィールドは、実行時に特定のIOタイプに設定することができます。 IOクラスで定義されているメソッドSetIOType()。このメソッドが呼び出されると、「ポート」フィールドがどのタイプを参照しているかがわかり、SetStrategy()メソッドを呼び出すと、実行されますIOクラスのいずれかでオーバーライドされたメソッド。
IOManagerクラスはクライアントです。特定のIOタイプ、たとえばSerialIOが必要な場合、IOクラスを新規に作成し、SerialIOクラスインスタンスを渡すことによってSetIOType()メソッドを呼び出すだけです。 、およびSerialIOタイプに関連するすべてのロジックが自動的に設定されます。
私の説明があなたを助けることを願っています。