107

私は自分の問題のいくつかを解決するためにGangOfFourを読んでいて、 Mediatorパターンに出くわしました。

以前、GUIアプリケーションを作成するためにプロジェクトでObserverを使用していました。両者の間に大きな違いが見当たらないので、少し混乱しています。私は違いを見つけるために閲覧しましたが、私の質問に対する適切な答えを見つけることができませんでした。

誰かが私が2つを明確に区別するいくつかの良い例で2つを区別するのを手伝ってくれるでしょうか?

4

8 に答える 8

113

オブザーバーパターン:オブジェクト間の1対多の依存関係を定義して、1つのオブジェクトの状態が変化したときに、そのすべての依存関係が自動的に通知および更新されるようにします。

メディエーターパターン:オブジェクトのセットがどのように相互作用するかをカプセル化するオブジェクトを定義します。Mediatorは、オブジェクトが相互に明示的に参照しないようにすることで緩い結合を促進し、オブジェクトの相互作用を個別に変更できるようにします。

出典:ドファクトリー

例:

オブザーバーパターン:クラスAは、タイプOのオブザーバーを0個以上登録できます。Aの何かが変更されると、すべてのオブザーバーに通知されます。

メディエーターパターン:クラスXのインスタンスがいくつかあり(またはいくつかの異なるタイプ:X、Y、Z)、それらは互いに通信したいと考えています(ただし、それぞれがそれぞれを明示的に参照することは望ましくありません)その他)、メディエータークラスMを作成します。Xの各インスタンスには、Mの共有インスタンスへの参照があり、それを介してXの他のインスタンス(またはX、Y、Z)と通信できます。

于 2012-02-10T11:02:49.180 に答える
46

オブザーバーとメディエーター、デザインパターン、再利用可能なオブジェクト指向ソフトウェアの要素という用語を作り出した元の本では、メディエーターパターンはオブザーバーパターンを使用して実装できると書かれています。ただし、同僚(オブザーバーパターンのサブジェクトとほぼ同等)にメディエータークラスまたはメディエーターインターフェイスのいずれかへの参照を持たせることによって実装することもできます。

オブザーバーパターンを使用したい場合が多くありますが、重要なのは、オブジェクトがその状態を監視している他のオブジェクトを認識してはならないということです。

メディエーターはもう少し具体的で、クラスが直接通信するのを避け、代わりにメディエーターを介して通信します。これは、通信を処理するだけのクラスに通信をオフロードできるようにすることで、単一責任の原則を支援します。

古典的なメディエーターの例はGUIにあり、単純なアプローチでは、ボタンクリックイベントに「Fooパネルが無効で、Barパネルに「日付を入力してください」というラベルが付いている場合はサーバーを呼び出さないでください」というコードが表示される場合があります。それ以外の場合は先に進んでください」、メディエーターパターンでは、「私は単なるボタンであり、FooパネルとBarパネルのラベルについて知っている地上のビジネスはないので、サーバーに電話するかどうかをメディエーターに尋ねます。今は大丈夫です。」

または、メディエーターがオブザーバーパターンを使用して実装されている場合、ボタンは「ねえ、オブザーバー(メディエーターを含む)、私の状態が変化しました(誰かが私をクリックしました)。気になったら何かしてください」と表示されます。私の例では、メディエーターを直接参照するよりもおそらく意味がありませんが、多くの場合、オブザーバーパターンを使用してメディエーターを実装することは理にかなっており、オブザーバーとメディエーターの違いは、コード自体の違いよりも意図の1つです。

于 2012-02-10T18:44:47.220 に答える
42

観察者

1.なし

  • Client1:ねえ件名、いつ変更しますか?

  • Client2 :件名を変更したのはいつですか。気づかなかった!

  • Client3 :件名が変更されたことを知っています。

2.と

  • クライアントは沈黙しています。
  • 今度いつか ...
  • 件名:親愛なるクライアント、私は変わりました!

メディエーター

1.なし

  • Client1:ねえタクシー1 、どこかに連れて行って。
  • Client2:ねえタクシー1、どこかに連れて行って。
  • Client1:ねえタクシー2、どこかに連れて行って。
  • Client2:ねえタクシー2、どこかに連れて行って。

2.と

  • Client1:ねえ、タクシーセンター、タクシーに乗ってください。
  • Client2:ねえ、タクシーセンター、タクシーに乗ってください。
于 2016-05-27T23:41:47.310 に答える
16

これらのパターンは、さまざまな状況で使用されます。

メディエーターパターンは、依存関係のある2つのサブシステムがあり、一方が変更される予定であり、他方に依存するシステムを変更したくない場合があるため、メディエーターを導入する場合に使用されます。それらの間の依存関係を切り離します。そうすれば、サブシステムの1つが変更されたときに、メディエーターを更新するだけで済みます。

オブザーバーパターンは、クラスが他のクラスが自分自身を登録し、イベント(ButtonListenerなど)で通知を受信できるようにする場合に使用されます。

これらのパターンはどちらも結合を少なくすることができますが、まったく異なります。

于 2012-02-10T10:56:15.143 に答える
13

例を見てみましょう。2つのアプリケーションを構築するとします。

  1. チャットアプリケーション。
  2. 緊急救急車オペレーターアプリケーション。

メディエーター

mediatorチャットアプリケーションを構築するには、デザインパターンを選択します。

  • その人はいつでもチャットに参加したりチャットから離れたりする可能性があるため、チャットしている2人の人を直接参照し続けることは意味がありません。
  • それでも、2人の間のコミュニケーションを促進し、チャットできるようにする必要があります。

なぜ私たちは好むの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);

違い:

  1. チャットmediatorには、人のオブジェクト間の双方向通信(送信と受信)があり、オペレーターobservableは一方向通信(救急車observerに運転して終了するように指示します)しかありません。
  2. チャットmediatorは、人のオブジェクトをそれらの間で相互作用させることができ(直接の通信でなくても)、救急車observersはオペレーターのobservableイベントにのみ登録されます。
  3. 各人物オブジェクトにはチャットへの参照がmediatorあり、チャットmediatorはすべての人物への参照を保持します。救急車observerがオペレーターへの参照を保持しない場合observable、オペレーターのみobservableがすべての救急車への参照を保持しますobserver
于 2018-07-15T18:02:31.393 に答える
8

どちらも状態の変化を体系的に伝えるために使用されますが、構造的および意味的にIMOがわずかに異なります。

オブザーバーは、オブジェクト自体から特定のオブジェクトの状態変化をブロードキャストするために使用されます。したがって、変更は、シグナリングも担当する中央オブジェクトで発生します。ただし、メディエーターでは、状態の変化はどのオブジェクトでも発生する可能性がありますが、メディエーターからブロードキャストされます。したがって、フローには違いがあります。しかし、これがコードの動作に影響を与えるとは思いません。同じ動作を実現するために、いずれかを使用できます。一方、この違いは、コードの概念的な理解に影響を与える可能性があります。

ほら、パターンを使用する主な目的は、開発者間で共通の言語を作成することです。したがって、メディエーターを見ると、通信ノイズを減らす(またはSRPを促進する)ために単一のブローカー/ハブを介して通信しようとしている複数の要素を個人的に理解しています。各オブジェクトは、状態変化を通知する機能を持つという点で等しく重要です。たとえば、空港に接近する複数の航空機について考えてみます。それぞれが互いに通信するのではなく、パイロン(メディエーター)を介して通信する必要があります。(着陸時に1000機の航空機が相互に通信していると考えてください-それは混乱になります)

ただし、オブザーバーを見ると、気になる状態変化がいくつかあることを意味し、特定の状態変化をリッスンするために登録/サブスクライブする必要があります。状態変化のシグナリングを担当する中心的なオブジェクトがあります。たとえば、AからBに向かう途中の特定の空港を気にする場合は、その空港に登録して、空の滑走路などがあるかのように放送されるイベントをキャッチできます。

それが明確であることを願っています。

于 2017-01-05T11:05:13.923 に答える
5

@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

説明:

  1. Eagleは、登録イベントを通じて最初にネットワークに追加されます。Eagleが最初のものであるため、他の同僚への通知はありません。
  2. オーストリッチがネットワークに追加されると、Eagle通知されます。出力の1行目がレンダリングされるようになりました。
  3. ペンギンがネットワークに追加されると、EagleOstrichの両方 に通知されます。出力の2行目と3行目がレンダリングされるようになりました。
  4. イーグルが登録解除イベントを通じてネットワークを離れたとき、ダチョウペンギンの両方に通知されました。出力の4行目と5行目がレンダリングされます。
于 2016-08-08T15:39:09.597 に答える
2

この説明について技術的には、オブザーバーとメディエーターは同じであり、コンポーネント通信の分離された方法を提供するために使用されますが、使用法は異なります。

サブスクライブされたコンポーネントに状態の変更(たとえば、新しいdbレコードの作成)をobeserver 通知する 一方で、コマンドはコンポーネントを登録して、ビジネスロジックフロー(パスワードのリセットのためにユーザーに電子メールを送信する)に関連する処理を実行します。mediator

観察者

  • 通知の消費者は、通知を受け取るためにサブスクライブする責任があります
  • 通知処理はビジネスフローの一部ではありません

メディエーター

  • 「発行者」と「消費者」を接続するには、明示的な登録が必要です
  • 通知処理は特定のビジネスフローの一部です
于 2018-05-23T08:39:23.063 に答える