私は自分の問題のいくつかを解決するためにGangOfFourを読んでいて、 Mediatorパターンに出くわしました。
以前、GUIアプリケーションを作成するためにプロジェクトでObserverを使用していました。両者の間に大きな違いが見当たらないので、少し混乱しています。私は違いを見つけるために閲覧しましたが、私の質問に対する適切な答えを見つけることができませんでした。
誰かが私が2つを明確に区別するいくつかの良い例で2つを区別するのを手伝ってくれるでしょうか?
私は自分の問題のいくつかを解決するためにGangOfFourを読んでいて、 Mediatorパターンに出くわしました。
以前、GUIアプリケーションを作成するためにプロジェクトでObserverを使用していました。両者の間に大きな違いが見当たらないので、少し混乱しています。私は違いを見つけるために閲覧しましたが、私の質問に対する適切な答えを見つけることができませんでした。
誰かが私が2つを明確に区別するいくつかの良い例で2つを区別するのを手伝ってくれるでしょうか?
オブザーバーパターン:オブジェクト間の1対多の依存関係を定義して、1つのオブジェクトの状態が変化したときに、そのすべての依存関係が自動的に通知および更新されるようにします。
メディエーターパターン:オブジェクトのセットがどのように相互作用するかをカプセル化するオブジェクトを定義します。Mediatorは、オブジェクトが相互に明示的に参照しないようにすることで緩い結合を促進し、オブジェクトの相互作用を個別に変更できるようにします。
出典:ドファクトリー
例:
オブザーバーパターン:クラスAは、タイプOのオブザーバーを0個以上登録できます。Aの何かが変更されると、すべてのオブザーバーに通知されます。
メディエーターパターン:クラスXのインスタンスがいくつかあり(またはいくつかの異なるタイプ:X、Y、Z)、それらは互いに通信したいと考えています(ただし、それぞれがそれぞれを明示的に参照することは望ましくありません)その他)、メディエータークラスMを作成します。Xの各インスタンスには、Mの共有インスタンスへの参照があり、それを介してXの他のインスタンス(またはX、Y、Z)と通信できます。
オブザーバーとメディエーター、デザインパターン、再利用可能なオブジェクト指向ソフトウェアの要素という用語を作り出した元の本では、メディエーターパターンはオブザーバーパターンを使用して実装できると書かれています。ただし、同僚(オブザーバーパターンのサブジェクトとほぼ同等)にメディエータークラスまたはメディエーターインターフェイスのいずれかへの参照を持たせることによって実装することもできます。
オブザーバーパターンを使用したい場合が多くありますが、重要なのは、オブジェクトがその状態を監視している他のオブジェクトを認識してはならないということです。
メディエーターはもう少し具体的で、クラスが直接通信するのを避け、代わりにメディエーターを介して通信します。これは、通信を処理するだけのクラスに通信をオフロードできるようにすることで、単一責任の原則を支援します。
古典的なメディエーターの例はGUIにあり、単純なアプローチでは、ボタンクリックイベントに「Fooパネルが無効で、Barパネルに「日付を入力してください」というラベルが付いている場合はサーバーを呼び出さないでください」というコードが表示される場合があります。それ以外の場合は先に進んでください」、メディエーターパターンでは、「私は単なるボタンであり、FooパネルとBarパネルのラベルについて知っている地上のビジネスはないので、サーバーに電話するかどうかをメディエーターに尋ねます。今は大丈夫です。」
または、メディエーターがオブザーバーパターンを使用して実装されている場合、ボタンは「ねえ、オブザーバー(メディエーターを含む)、私の状態が変化しました(誰かが私をクリックしました)。気になったら何かしてください」と表示されます。私の例では、メディエーターを直接参照するよりもおそらく意味がありませんが、多くの場合、オブザーバーパターンを使用してメディエーターを実装することは理にかなっており、オブザーバーとメディエーターの違いは、コード自体の違いよりも意図の1つです。
Client1:ねえ件名、いつ変更しますか?
Client2 :件名を変更したのはいつですか。気づかなかった!
Client3 :件名が変更されたことを知っています。
これらのパターンは、さまざまな状況で使用されます。
メディエーターパターンは、依存関係のある2つのサブシステムがあり、一方が変更される予定であり、他方に依存するシステムを変更したくない場合があるため、メディエーターを導入する場合に使用されます。それらの間の依存関係を切り離します。そうすれば、サブシステムの1つが変更されたときに、メディエーターを更新するだけで済みます。
オブザーバーパターンは、クラスが他のクラスが自分自身を登録し、イベント(ButtonListenerなど)で通知を受信できるようにする場合に使用されます。
これらのパターンはどちらも結合を少なくすることができますが、まったく異なります。
例を見てみましょう。2つのアプリケーションを構築するとします。
mediatorチャットアプリケーションを構築するには、デザインパターンを選択します。
なぜ私たちは好むのmediatorでしょうか?その定義を見てください:
メディエーターパターンを使用すると、オブジェクト間の通信はメディエーターオブジェクト内にカプセル化されます。オブジェクトは相互に直接通信するのではなく、メディエーターを介して通信します。これにより、通信するオブジェクト間の依存関係が減少し、それによって結合が減少します。
魔法はどのように機能しますか?まず、チャットメディエーターを作成し、人物オブジェクトを登録します。これにより、すべての人物と双方向で接続されます(チャットメディエーターを使用してメッセージを送信すると、チャットメディエーターにアクセスでき、チャットメディエーターがアクセスします。個人オブジェクトの受信したメソッドは、彼もそれにアクセスできるため)
function Person(name) {
let self = this;
this._name = name;
this._chat = null;
this._receive(from, message) {
console.log("{0}: '{1}'".format(from.name(), message));
}
this._send(to, message) {
this._chat.message(this, to, message);
}
return {
receive: (from, message) => { self._receive(from, message) },
send: (to, message) => { self._send(to, message) },
initChat: (chat) => { this._chat = chat; },
name: () => { return this._name; }
}
}
function ChatMediator() {
let self = this;
this._persons = [];
return {
message: function (from, to, message) {
if (self._persons.indexOf(to) > -1) {
self._persons[to].receive(from, message);
}
},
register: function (person) {
person.initChat(self);
self._persons.push(person);
}
unRegister: function (person) {
person.initChat(null);
delete self._persons[person.name()];
}
}
};
//Usage:
let chat = new ChatMediator();
let colton = new Person('Colton');
let ronan = new Person('Ronan');
chat.register(colton);
chat.register(ronan);
colton.send(colton, 'Hello there, nice to meet you');
ronan.send(ronan, 'Nice to meet you to');
colton.send(colton, 'Goodbye!');
chat.unRegister(colton);
observer911コールアプリケーションを構築するには、デザインパターンを選択します。
observerのオブジェクトは、緊急事態が発生したときに通知を受けることを望んでいるため、彼は住所を運転して支援を提供できます。observableは救急車の各オンを参照し続けobservers、助けが必要なとき(またはイベントを生成するとき)に通知します。なぜ私たちは好むのobserverでしょうか?その定義を見てください:
サブジェクトと呼ばれるオブジェクトは、オブザーバーと呼ばれるその依存関係のリストを維持し、通常はメソッドの1つを呼び出すことによって、状態の変化を自動的に通知します。
function AmbulanceObserver(name) {
let self = this;
this._name = name;
this._send(address) {
console.log(this._name + ' has been sent to the address: ' + address);
}
return {
send: (address) => { self._send(address) },
name: () => { return this._name; }
}
}
function OperatorObservable() {
let self = this;
this._ambulances = [];
return {
send: function (ambulance, address) {
if (self._ambulances.indexOf(ambulance) > -1) {
self._ambulances[ambulance].send(address);
}
},
register: function (ambulance) {
self._ambulances.push(ambulance);
}
unRegister: function (ambulance) {
delete self._ambulances[ambulance.name()];
}
}
};
//Usage:
let operator = new OperatorObservable();
let amb111 = new AmbulanceObserver('111');
let amb112 = new AmbulanceObserver('112');
operator.register(amb111);
operator.register(amb112);
operator.send(amb111, '27010 La Sierra Lane Austin, MN 000');
operator.unRegister(amb111);
operator.send(amb112, '97011 La Sierra Lane Austin, BN 111');
operator.unRegister(amb112);
mediatorには、人のオブジェクト間の双方向通信(送信と受信)があり、オペレーターobservableは一方向通信(救急車observerに運転して終了するように指示します)しかありません。mediatorは、人のオブジェクトをそれらの間で相互作用させることができ(直接の通信でなくても)、救急車observersはオペレーターのobservableイベントにのみ登録されます。mediatorあり、チャットmediatorはすべての人物への参照を保持します。救急車observerがオペレーターへの参照を保持しない場合observable、オペレーターのみobservableがすべての救急車への参照を保持しますobserver。どちらも状態の変化を体系的に伝えるために使用されますが、構造的および意味的にIMOがわずかに異なります。
オブザーバーは、オブジェクト自体から特定のオブジェクトの状態変化をブロードキャストするために使用されます。したがって、変更は、シグナリングも担当する中央オブジェクトで発生します。ただし、メディエーターでは、状態の変化はどのオブジェクトでも発生する可能性がありますが、メディエーターからブロードキャストされます。したがって、フローには違いがあります。しかし、これがコードの動作に影響を与えるとは思いません。同じ動作を実現するために、いずれかを使用できます。一方、この違いは、コードの概念的な理解に影響を与える可能性があります。
ほら、パターンを使用する主な目的は、開発者間で共通の言語を作成することです。したがって、メディエーターを見ると、通信ノイズを減らす(またはSRPを促進する)ために単一のブローカー/ハブを介して通信しようとしている複数の要素を個人的に理解しています。各オブジェクトは、状態変化を通知する機能を持つという点で等しく重要です。たとえば、空港に接近する複数の航空機について考えてみます。それぞれが互いに通信するのではなく、パイロン(メディエーター)を介して通信する必要があります。(着陸時に1000機の航空機が相互に通信していると考えてください-それは混乱になります)
ただし、オブザーバーを見ると、気になる状態変化がいくつかあることを意味し、特定の状態変化をリッスンするために登録/サブスクライブする必要があります。状態変化のシグナリングを担当する中心的なオブジェクトがあります。たとえば、AからBに向かう途中の特定の空港を気にする場合は、その空港に登録して、空の滑走路などがあるかのように放送されるイベントをキャッチできます。
それが明確であることを願っています。
@cdcは、意図の違いをうまく説明しました。
その上にさらに情報を追加します。
オブザーバー:1つのオブジェクトのイベントを異なるオブジェクトのセット(異なるクラスのインスタンス)に通知できるようにします
メディエーター:特定のクラスから作成されたオブジェクトのセット間の通信を一元化します。
dofactoryからのメディエーターパターンの構造:
メディエーター:同僚間のコミュニケーションのためのインターフェースを定義します。
同僚:同僚間で通信されるイベントを定義する抽象クラスです。
ConcreteMediator :同僚のオブジェクトを調整して協調動作を実装し、同僚を維持します
ConcreteColleague :他の同僚によって生成されたMediatorを介して受信した通知操作を実装します
1つの実例:
メッシュトポロジでコンピューターのネットワークを維持しています。新しいコンピューターが追加された場合、または既存のコンピューターが削除された場合、そのネットワーク内の他のすべてのコンピューターは、これら2つのイベントについて認識している必要があります。
Mediatorパターンがどのように適合するかを見てみましょう。
コードスニペット:
import java.util.List;
import java.util.ArrayList;
/* Define the contract for communication between Colleagues.
Implementation is left to ConcreteMediator */
interface Mediator{
public void register(Colleague colleague);
public void unregister(Colleague colleague);
}
/* Define the contract for notification events from Mediator.
Implementation is left to ConcreteColleague
*/
abstract class Colleague{
private Mediator mediator;
private String name;
public Colleague(Mediator mediator,String name){
this.mediator = mediator;
this.name = name;
}
public String toString(){
return name;
}
public abstract void receiveRegisterNotification(Colleague colleague);
public abstract void receiveUnRegisterNotification(Colleague colleague);
}
/* Process notification event raised by other Colleague through Mediator.
*/
class ComputerColleague extends Colleague {
private Mediator mediator;
public ComputerColleague(Mediator mediator,String name){
super(mediator,name);
}
public void receiveRegisterNotification(Colleague colleague){
System.out.println("New Computer register event with name:"+colleague+
": received @"+this);
// Send further messages to this new Colleague from now onwards
}
public void receiveUnRegisterNotification(Colleague colleague){
System.out.println("Computer left unregister event with name:"+colleague+
":received @"+this);
// Do not send further messages to this Colleague from now onwards
}
}
/* Act as a central hub for communication between different Colleagues.
Notifies all Concrete Colleagues on occurrence of an event
*/
class NetworkMediator implements Mediator{
List<Colleague> colleagues = new ArrayList<Colleague>();
public NetworkMediator(){
}
public void register(Colleague colleague){
colleagues.add(colleague);
for (Colleague other : colleagues){
if ( other != colleague){
other.receiveRegisterNotification(colleague);
}
}
}
public void unregister(Colleague colleague){
colleagues.remove(colleague);
for (Colleague other : colleagues){
other.receiveUnRegisterNotification(colleague);
}
}
}
public class MediatorPatternDemo{
public static void main(String args[]){
Mediator mediator = new NetworkMediator();
ComputerColleague colleague1 = new ComputerColleague(mediator,"Eagle");
ComputerColleague colleague2 = new ComputerColleague(mediator,"Ostrich");
ComputerColleague colleague3 = new ComputerColleague(mediator,"Penguin");
mediator.register(colleague1);
mediator.register(colleague2);
mediator.register(colleague3);
mediator.unregister(colleague1);
}
}
出力:
New Computer register event with name:Ostrich: received @Eagle
New Computer register event with name:Penguin: received @Eagle
New Computer register event with name:Penguin: received @Ostrich
Computer left unregister event with name:Eagle:received @Ostrich
Computer left unregister event with name:Eagle:received @Penguin
説明:
この説明について技術的には、オブザーバーとメディエーターは同じであり、コンポーネント通信の分離された方法を提供するために使用されますが、使用法は異なります。
サブスクライブされたコンポーネントに状態の変更(たとえば、新しいdbレコードの作成)をobeserver 通知する 一方で、コマンドはコンポーネントを登録して、ビジネスロジックフロー(パスワードのリセットのためにユーザーに電子メールを送信する)に関連する処理を実行します。mediator