web-dev-qa-db-ja.com

コードでメッセージ/イベントバスを使用する理由

メッセージ/イベントバスについて聞いたことがあると思います。これは、システム内のすべてのイベントが流れる単一の場所です。同様のアーキテクチャは、コンピューターのマザーボードとLANネットワークにあります。配線の数を減らすため、マザーボードやネットワークに適したアプローチですが、ソフトウェア開発には適していますか?電子機器のような制限はありません。

メッセージバス/イベントバスの最も単純な実装は次のようになります。

class EventBus {
    void addListener(EventBusListener l}{...}
    void fireEvent(Event e) {...}
}

イベントのポストはbus.fireEvent(event)で行われ、メッセージの受信はbus.addListener(listener)で有効になります。そのようなアーキテクチャは、ソフトウェア開発に使用されることがあります。たとえば、MVP4GはGWTに同様のメッセージバスを実装します。

アクティブなプロジェクト:

休眠/死のプロジェクト:

「グローバル」に作成された人気のあるオブザーバー(リスナー)パターンです-システム内の各オブジェクトは各メッセージをリッスンできますが、それは悪いと思います。一部のオブジェクトには新しいタイプのメッセージが必要です。イベントバスは、たとえば新しいリスナークラスまたはリスナークラスに新しいメソッドを追加するために変更する必要があります。

これらの理由から、ほとんどのソフトウェアでは、Observerパターンはイベントバスよりも優れていると思います。イベントバスについてどう思いますか、それは典型的なアプリケーションにとって意味がありますか?

編集:ESBのような「大規模な」エンタープライズソリューションについては話していない-役に立つ可能性がある(さらに、ESBが提供するのはイベントバスだけではありません) '通常' Javaオブジェクト間接続のコードでメッセージバスを使用することの有用性について尋ねています-一部の人々はそれを行います。上記のリンクを確認してください。通常、ネットワーク内の各テレフォン(またはコンピューター)は相互に通信でき、バスはワイヤの数を減らすため、電話間通信またはコンピューター間通信。オブジェクトは-3、5を持つことができますか?

60
iirekm

ファサードパターン または メディエーターパターン の具体化であるため、これを好む人もいます。ロギング、アラート、モニタリング、セキュリティなどの分野横断的なアクティビティを集中化します。

多くの場合、シングルトン障害ポイントであるため、これを好まない人もいます。誰もがそれについて知る必要があります。

26
duffymo

私は、通常のJavaコードにメモリ内イベントバスを使用することを検討しています。

システム内の各オブジェクトは各メッセージをリッスンできますが、それは悪いことだと思います。カプセル化の原則を破っています(各オブジェクトはすべてを知っています)

これが本当に正しいかどうかはわかりません。クラスはイベントバスに登録する必要があります。オブザーバーパターンと同様に、クラスがイベントバスに登録されると、適切な署名と注釈を持つメソッドのみが通知されます。

単一責任の原則(たとえば、一部のオブジェクトが新しいタイプのメッセージを必要とする場合、新しいリスナークラスまたはリスナークラスに新しいメソッドを追加するために、イベントバスを頻繁に変更する必要があります)。

私は全く同意しません

イベントバスは頻繁に変更する必要があります

イベントバスは変更されません

同意する

add a new Listener class or a new method in the Listener class

これはどのようにSRPを壊しますか?私のBook Entityに関連するすべてのイベントをサブスクライブするBookEventListenerを持つことができます。はい、このクラスにメソッドを追加できますが、このクラスは凝集しています...

なぜそれを使用する予定ですか?ドメインの「いつ」をモデル化するのに役立ちます....

通常、メールを送信するようなものが聞こえます "when"本が購入されます

私たちは書き留めます

book.purchase();
sendEmail()

次に、本が購入されたときに監査ログを追加するよう指示され、上記のスニペットに移動します

book.purchase();
sendEmail();
**auditBook();**

すぐそこ [〜#〜] ocp [〜#〜] 違反

私は好む

book.purchase();
EventBus.raiseEvent(bookPurchasedEvent);

その後、必要に応じてハンドラーを追加し続けます

ありがとう

66
Sudarshan

JavaScriptで頻繁に使用します。非常に多くのさまざまなウィジェットが存在する可能性があり、他の何かが発生するたびに何らかのアクションを実行する必要があります。オブジェクトの所有権の本当の階層はありません。すべてのオブジェクトの参照をすべてのオブジェクトに渡すか、すべてのオブジェクトをグローバルにする代わりに、特定のウィジェット内で何か重大なことが発生した場合、「/ thisWidget/somethingHappened」を発行できます。他のウィジェットのAPIへ。 Java Springフレームワーク。このクラスにはすべてのウィジェットへの参照が含まれます。そして、すべての「配線」または「配管」を含む単一のクラスがあります。さまざまなイベントが発生した後に起こることのすべてのコードがあります。

一元化されており、アクセスと保守が簡単で、何かが変わったり、特定のイベントで新しいプロセスを発生させたい場合は、すべてのクラス/オブジェクト/ウィジェットを検索して、どこで何かを見つけようとする必要はありません処理中です。特定のイベントが発生したときにすべての「配線」を処理する「演算子」クラスに移動し、そのイベントのすべての影響を確認できます。このシステムでは、個々のウィジェットはすべて、他のウィジェットとは完全にAPIに依存しません。それは、何が起こったか、または何をしているかを単に公開します。

16
Bal

私はあなたが何をしているのか本当に理解するのに苦労しています。実際に別の名前を持つObservableである単純なイベントバスの例を挙げてから、次のように言います。

これらの理由から、ほとんどのソフトウェアでは、Observerパターンはイベントバスよりも優れていると思います。イベントバスについてどう思いますか、それは典型的なアプリケーションにとって意味がありますか?

..しかし、あなたの例を考えると、それらは同じです。これは、Enterprise Service Busのようなものを使用したことがあるかと思います。基本レベルでは、ESBはオブザーバーパターンと同じことを論理的に行いますが、市販製品ははるかに多くを追加します。ステロイドのイベントバスのようなものです。それらは複雑なソフトウェア製品であり、提供しています。

メッセージピックアップ
さまざまなエンドポイントをリッスンしてイベントを生成します。エンドポイントは、リスナー(HTTPサーバーなど)、メッセージングシステム(JMSなど)、データベース、または必要なものになります。

メッセージルーティング
イベントを取り、1つ/多くのエンドポイントに送信します。ルーティングは非常に賢く、バスはメッセージの種類、メッセージの内容、またはその他の条件に応じてメッセージをルーティングする場合があります。ルーティングはインテリジェントで動的にすることができます。

メッセージ変換
メッセージを別の形式に変換します。これは、XMLからJSONへの変換、またはデータベーステーブルの行からHTTPリクエストへの変換と同じくらい簡単です。日付形式の交換など、データ自体で変換が発生する可能性があります。

データエンリッチメント
途中でサービスを呼び出して、メッセージのデータを追加または変更します。たとえば、メッセージに郵便番号が含まれている場合、バスは郵便番号検索サービスを使用して住所データを追加できます。

..そしてもっとたくさん。詳細を調べ始めると、実際にwhy人がこれらのものを使用していることがわかります。

14
Qwerky

アプリケーションモジュールをサービスベースのアーキテクチャに分離する重要なステップになる可能性があるためです。

そのため、アプリケーションのモジュールを分離されたサービスに分離する意図がない場合、Observer patternのネイティブ実装により、よりシンプルなソリューション。

しかし、ビルドしたい場合は、マイクロサービスアーキテクチャと言いましょうイベントバスを介して接続されているだけなので、アプリケーションの一部のみを他の人に影響を与えずに更新およびデプロイします。

したがって、ここでの主な質問は、アプリケーションコンポーネントのデカップリングの望ましいレベルです。

それに関するいくつかの参照:

4
brunocrt

良い例えは、電話交換機の例です。ここでは、すべてのハンドセットが他のすべてのハンドセットにダイヤルできます。侵害されたハンドセットは、他の会話に同調する可能性があります。ワイヤーのようなプログラム制御フロー(サイクロマティックな複雑さは誰でも!)これは、2つのエンドポイント間に接続/物理媒体を置くという要件に似ています。これは、N個のハンドセットの場合、N個のフローを取得する傾向がある新しいハンドセットごとにNC2(組み合わせロジック)フローを使用するのではなく、Soです。

複雑さの削減は、コードが理解しやすいことを意味します。強調した重要なポイントから始めましょう。1.グローバルな知識2.侵入的な修正。

グローバルな知識:メッセージイベントをエンベロープと見なします。イベントハンドラー/送信者の観点からは、公開されているデータはなく、エンベロープが表示されています(派生クラスが「instanceof」チェックを使用して何らかの検査を試行しない限り)。適切なOOP=デザインでは、これは発生しません。

侵入的な変更:イベント固有のリスナーアプローチを使用する代わりに、グローバルイベント処理アプローチを使用できます。そのため、グローバルイベントタイプがあります(このイベントタイプでは、データがピギーバックされ、ダウンキャストされます)。これは、JavaのPropertyBeanSupportモデルによく似ています。単一のイベントタイプでは、単一の送信者タイプとリスナータイプが必要です。これは、何か新しいものを見るたびにバス/リスナーを変更する必要がないことを意味します。 patternいダウンキャストは、Adapterパターンを使用して和らげることができます(別のレベルのリダイレクト見積もりを開始しないでください!)。プログラマは、任意の言語でアセンブリを作成できます。そのため、常識とスマートさの必要性を置き換えることはできません。私が述べたいのは、それが効果的なツールになり得るということです。

実際のイベントレシーバーは、リスナー(構成/プロキシ)を簡単に使用できます。このようなJavaコードベースでは、リスナーはスタンドアロンの内部クラス宣言のように見えます(特定のIDEで未使用の警告がフラグ付けされます)。プレーヤーはボールを見るまで反応しません。

「@duffymo」は別の興味深い側面を指摘しています:「単一障害点」。これは、理論上、メモリ(RAM)に存在し、MessageHandlersに固有ではないオブジェクトに影響します。

2
questzen

実際の例として、アプリはx分ごとにWebサービスと同期します。新しいデータを受信した場合は、GUIを更新する必要があります。これで、SyncAdapterはバックグラウンドスレッドで実行されるため、単にテキストビューをポイントしてそのプロパティを変更することはできず、イベントをバブルする必要があります。そして、そのイベントを確実にキャッチする唯一の方法は、サブスクライバーが処理するためにそのイベントを渡す共有(静的、シングルトン)オブジェクトがある場合です。

1