web-dev-qa-db-ja.com

消費者が多くのプロデューサーにサブスクライブする場合の適切なイベント駆動型設計

クラスのインスタンスが多数あります。これらのインスタンスはイベントを発生させることができます。そのイベントで唯一重要なことは、どのインスタンスがそれを発生させたかです。

そのイベントをサブスクライブしたいクラスの別のインスタンス(そして将来的にはさらに多くのクラス)があります。

例(疑似C#の場合):

class Button
{
   public event EventHandler<Button> PressedTheButtonEvent();
}

class EventConsumer
{
   private int buttonsPressed;
   private OnButtonPressed(Button buttonReference)
   {
      buttonReference.color = blue;
      buttonsPressed++;
   }
}

EventConsumerクラスがイベントにサブスクライブするには、すべてのインスタンスのEventHandler<Button>デリゲートにサブスクライブする必要があります。私はボタンの数が多いという事実を考えると、既存のすべてのボタンを繰り返してサブスクライブするのは好きではありません。これはエラーが発生しやすく、特に、ボタンを繰り返し処理した後に何らかの理由でボタンが作成された場合はそうです。

私は2つの解決策を考え出しました:

  1. PressedTheButtonEventを静的にします。
  2. 単一のイベントを持つ別のクラスを作成します。すべてのボタンがそのイベントを起動する代わりに、新しいクラスのメソッドを呼び出して起動します。

新しいクラスはButtonクラスと緊密に結合されているため、2。は好きではありません。また、1。はあまり好きではありません。何らかの理由で、誰かが静的イベントのサブスクライブを解除するのを忘れた場合、それらのインスタンスはガベージコレクションされないためです。

他に解決策はありますか?

私はC#を書いていますが、質問をより一般的な文脈で維持しようとしました。

1

2はメディエーターパターンです。

次の方法で密結合を回避できます。

  • 'publish'と 'listen'だけでIMediatorインターフェースを挿入する

  • ファクトリメソッドを使用してボタンを作成し、同時にイベントをワイヤリングする

  • 挿入されたメディエーター、イベントを取得し、ボタンを含むButtonViewに接続するButtonViewModelを用意します。

WpfでC#を実行している場合は、viewmodelが最適です。

1
Ewan

イベントハンドラーにはラムダを使用します。ラムダは、作成時に機能するために必要なすべての情報をキャプチャします。

これは、ループに数値ボタンを追加する古典的な電卓の例のようなものです。

for (n in 0 .. 9)
{
    let button = createNumberButton(n);
    button.Click += (source, event) => { numberButtonPressed(n); };
}

イベント処理のためにボタンのリストを繰り返す必要はなく、インスタンスのIDハッシュマップを設定する必要もありません。 :)

1
yeoman