web-dev-qa-db-ja.com

Mediator Vs Observerオブジェクト指向のデザインパターン

私はいくつかの問題を解決するためにGang Of Fourを読んでいて、Mediatorパターン。

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

明確に2つを区別するいくつかの良い例で、2つを区別するのに役立つ人がいますか?

83
Fooo

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

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

ソース: dofactory

例:

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

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

96
cdc

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

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

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

古典的なMediatorの例はGUIです。単純なアプローチでは、「Fooパネルが無効で、Barパネルに「日付を入力してください」というラベルがある場合、サーバーを呼び出さないでください」 Mediatorパターンでは、「私はただのボタンであり、FooパネルとBarパネルのラベルについて何も知らないので、サーバーを呼び出すかどうかをメディエーターに尋ねる」と言うことができます。今は大丈夫です。」

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

33
psr

観察者

1.なし

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

  • Client2:いつ変更しましたかSubject?気づいていない!

  • ClientSubjectが変更されたことを知っています。

2.あり

  • クライアントはサイレントです。
  • 今度いつか ...
  • 対象:親愛なるクライアント、私は変更しました!

調停者

1.なし

  • Client1:やあTaxi1.
  • Client2:やあTaxi1.
  • Client1:やあTaxi2.
  • Client2:やあTaxi2.

2.あり

  • Client1:やあTaxiCenter、私を連れて行ってくださいTaxi
  • Client2:ちょっとTaxiCenter、私を連れて行ってくださいTaxi
30
Pmpr

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

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

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

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

13
uzilan

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

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

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

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

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

5
stdout

例を見てみましょう。2つのアプリケーションを構築することを検討してください。

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

調停者

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

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

なぜmediatorを好むのでしょうか?その定義を見てください:

メディエーターパターンを使用すると、オブジェクト間の通信はメディエーターオブジェクト内にカプセル化されます。オブジェクトは相互に直接通信するのではなく、メディエーターを介して通信します。これにより、通信するオブジェクト間の依存関係が減少し、それにより結合が減少します。

魔法はどのように機能しますか?最初にチャットメディエーターを作成し、personオブジェクトを登録します。そのため、すべての個人と双方向の接続を持ちます(チャットメディエーターを使用してメッセージを送信でき、チャットメディエーターがアクセスできます。個人オブジェクトの受信メソッドにより、彼もアクセスできるようになります)

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);

観察者

911コールアプリケーションを構築するには、observerデザインパターンを選択します。

  • 各救急車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は、personオブジェクト(送受信)間の双方向通信を持ち、オペレーターobservableは片道通信のみを行います(救急車observerに運転と終了を伝えます) )。
  2. チャットmediatorは、(直接通信ではない場合でも)人物オブジェクト間で相互作用させることができ、救急車observersはオペレーターobservableイベントにのみ登録します。
  3. 各人物オブジェクトには、チャットmediatorへの参照があり、チャットmediatorもすべての人物への参照を保持しています。救急車observerがオペレーターobservableへの参照を保持しない場合、オペレーターobservableのみがすべての救急車observerへの参照を保持します。
4
Shahar Shokrani

@cdcは意図の違いを非常にうまく説明しました。

さらに情報を追加します。

Observer :1つのオブジェクトのイベントを異なるオブジェクトのセット(異なるクラスのインスタンス)に通知できるようにします

Mediator :特定のクラスから作成されたオブジェクトのセット間の通信を集中化します。

dofactory のMediatorパターンの構造:

enter image description here

Mediator:同僚間の通信のためのインターフェースを定義します。

同僚:同僚間で通信されるイベントを定義する抽象クラスです

ConcreteMediatorColleagueオブジェクトを調整し、同僚を維持することにより、協調動作を実装します

ConcreteColleagueMediatorを介して受信した通知操作を実装します。これは他のColleague

1つの実世界の例:

Meshトポロジーでコンピューターのネットワークを維持しています。新しいコンピューターが追加された場合、または既存のコンピューターが削除された場合、そのネットワーク内の他のすべてのコンピューターはこれら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. Ostrichがネットワークに追加されると、Eagleが通知されます。出力の1行目がレンダリングされます。
  3. ペンギンがネットワークに追加されると、EagleOstrichの両方が通知されます:出力の2行目と3行目がレンダリングされます。
  4. Eagleが登録解除イベントによってネットワークを離れたとき、OstrichPenguinの両方が通知されました。出力の4行目と5行目がレンダリングされます。
4
Ravindra babu

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

obeservernotifiessubscribed状態の変化に関するコンポーネント(たとえば、新しいdbレコードの作成)、 mediatorコマンドregisteredコンポーネントは、ビジネスロジックフロー(パスワードリセットのためにユーザーに電子メールを送信)に関連する何かを行う)。

観察者

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

調停者

  • 「パブリッシャー」と「コンシューマー」を接続するために必要な明示的な登録
  • 通知処理は特定のビジネスフローの一部です
1