Mediator Vs Observer Objektorientierte Entwurfsmuster

91

Ich habe die Viererbande gelesen , um einige meiner Probleme zu lösen, und bin auf das Mediator- Muster gestoßen .

Ich hatte Observer zuvor in meinen Projekten verwendet, um eine GUI-Anwendung zu erstellen. Ich bin etwas verwirrt, da ich keinen großen Unterschied zwischen den beiden finde. Ich habe nach dem Unterschied gesucht, aber keine passende Antwort auf meine Frage gefunden.

Könnte mir jemand helfen, mit einem guten Beispiel zwischen den beiden zu unterscheiden, das die beiden klar abgrenzt?

Fooo
quelle
5
Meine Anfrage, diese Frage zu migrieren, Programmers.StackExchangewurde abgelehnt, aber ich habe dort einen ähnlichen Beitrag verfasst, weil ich an der Antwort interessiert war. Vielleicht finden Sie einige der Antworten interessant. :)
Rachel
Beispiele für JavaScript finden Sie in meiner Antwort auf eine ähnliche Frage .
Alex Pakka
Das ursprüngliche GoF-Buch behandelt dies unter Punkt 8 der Implementierung, indem es ein Beispiel für a ChangeManagerfür das verwendete ObserverMuster gibt Mediator. sehen; paginas.fe.up.pt/~aaguiar/as/gof/hires/pat5g.htm#samplecode
robi-y

Antworten:

104

Das Beobachtermuster: Definiert eine Eins-zu-Viele-Abhängigkeit zwischen Objekten, sodass alle abhängigen Objekte automatisch benachrichtigt und aktualisiert werden, wenn ein Objekt seinen Status ändert.

Das Mediator-Muster: Definieren Sie ein Objekt, das die Interaktion einer Gruppe von Objekten zusammenfasst. Mediator fördert die lose Kopplung, indem verhindert wird, dass Objekte explizit aufeinander verweisen, und Sie können ihre Interaktion unabhängig voneinander variieren.

Quelle: dofactory

Beispiel:

Das Beobachtermuster: Klasse A kann null oder mehr Beobachter vom Typ O registrieren lassen. Wenn etwas in A geändert wird, werden alle Beobachter benachrichtigt.

Das Mediatormuster: Sie haben einige Instanzen der Klasse X (oder vielleicht sogar mehrere verschiedene Typen: X, Y und Z), und sie möchten miteinander kommunizieren (aber Sie möchten nicht, dass jeder explizite Verweise auf jeden hat other), sodass Sie eine Mediatorklasse M erstellen. Jede Instanz von X hat einen Verweis auf eine gemeinsam genutzte Instanz von M, über die sie mit den anderen Instanzen von X (oder X, Y und Z) kommunizieren kann.

CDC
quelle
Die Erklärung des Beobachters scheint eher dem Befehlsmuster als dem Beobachtermuster nahe zu sein
Aun
39

In dem ursprünglichen Buch, in dem die Begriffe Beobachter und Mediator, Entwurfsmuster, Elemente wiederverwendbarer objektorientierter Software geprägt wurden , heißt es, dass das Mediator-Muster mithilfe des Beobachtermusters implementiert werden kann. Es kann jedoch auch implementiert werden, indem Kollegen (die in etwa den Subjekten des Beobachtermusters entsprechen) entweder auf eine Mediator-Klasse oder eine Mediator-Schnittstelle verweisen.

Es gibt viele Fälle, in denen Sie das Beobachtermuster verwenden möchten. Der Schlüssel ist, dass ein Objekt nicht wissen sollte, welche anderen Objekte seinen Zustand beobachten.

Mediator ist etwas spezifischer, es wird vermieden, dass Klassen direkt kommunizieren, sondern über einen Mediator. Dies unterstützt das Prinzip der Einzelverantwortung, indem die Kommunikation in eine Klasse verlagert werden kann, die nur die Kommunikation übernimmt.

Ein klassisches Mediator-Beispiel ist eine grafische Benutzeroberfläche, in der der naive Ansatz dazu führen kann, dass bei einem Klickereignis mit Schaltflächen ein Code angezeigt wird, der besagt: "Wenn das Foo-Bedienfeld deaktiviert ist und das Balkenbedienfeld die Bezeichnung" Bitte Datum eingeben "trägt, rufen Sie den Server nicht an. Andernfalls machen Sie weiter ", wo mit dem Mediator-Muster steht:" Ich bin nur ein Knopf und habe nichts mit dem Foo-Panel und dem Etikett auf dem Bar-Panel zu tun. Deshalb frage ich meinen Mediator, ob ich den Server anrufe ist jetzt in Ordnung. "

Wenn Mediator mithilfe des Observer-Musters implementiert wird, lautet die Schaltfläche "Hey, Beobachter (einschließlich des Mediators), mein Status hat sich geändert (jemand hat mich angeklickt). Tun Sie etwas dagegen, wenn Sie sich darum kümmern". In meinem Beispiel ist dies wahrscheinlich weniger sinnvoll, als direkt auf den Mediator zu verweisen. In vielen Fällen wäre es jedoch sinnvoll, das Observer-Muster zur Implementierung von Mediator zu verwenden, und der Unterschied zwischen Observer und Mediator ist eher eine Absicht als ein Unterschied im Code selbst.

psr
quelle
Ich habe nach diesem Wort "Prinzip der Einzelverantwortung" gesucht.
Stdout
37

Beobachter

1. Ohne

  • Client1 : Hey Betreff , wann wechselst du?

  • Client2 : Wann haben Sie den Betreff gewechselt ? Das habe ich nicht bemerkt!

  • Client3 : Ich weiß, dass sich das Thema geändert hat.

2. Mit

  • Kunden schweigen.
  • Etwas später ...
  • Betreff : Sehr geehrte Kunden , ich habe mich verändert!

Vermittler

1. Ohne

  • Client1 : Hey Taxi1 , bring mich wo hin.
  • Client2 : Hey Taxi1 , bring mich wo hin.
  • Client1 : Hey Taxi2 , bring mich wo hin.
  • Client2 : Hey Taxi2 , bring mich wo hin.

2. Mit

  • Client1 : Hey TaxiCenter , bitte nimm mir ein Taxi .
  • Client2 : Hey TaxiCenter , bitte nimm mir ein Taxi .
Pmpr
quelle
2
Ihr Mediator-Beispiel ist das Fabrikmuster, nicht das Mediatormuster
Mohammad Karimi,
2
@Pmpr Es ist ein Mediator-Designmuster. TaxiCenter erstellt keine Taxis, stellt die Taxis auf irgendeine Weise zur Verfügung (wahrscheinlich wartet jedes Taxi, bis TaxiCenter sagt, dass Sie an der Reihe sind)
Siva R
14

Diese Muster werden in verschiedenen Situationen verwendet:

Das Mediatormuster wird verwendet, wenn Sie zwei Subsysteme mit einer gewissen Abhängigkeit haben und eines davon geändert werden muss. Da Sie das vom anderen abhängige System möglicherweise nicht ändern möchten, möchten Sie möglicherweise einen Mediator einführen, der dies tut entkoppeln Sie die Abhängigkeit zwischen ihnen. Auf diese Weise müssen Sie den Mediator nur aktualisieren, wenn sich eines der Subsysteme ändert.

Das Beobachtermuster wird verwendet, wenn eine Klasse anderen Klassen erlauben möchte, sich zu registrieren und Benachrichtigungen über Ereignisse zu erhalten, z. B. ButtonListener usw.

Beide Muster ermöglichen eine geringere Kopplung, sind jedoch sehr unterschiedlich.

Uzilan
quelle
6

Gehen wir an einem Beispiel vorbei: Stellen Sie sich vor, Sie möchten zwei Anwendungen erstellen:

  1. Chat-Anwendung.
  2. Anwendung des Rettungswagenbetreibers.

Vermittler

Wenn Sie die Chat-Anwendung erstellen, wählen Sie das mediatorEntwurfsmuster aus.

  • Die Personen können jederzeit dem Chat beitreten und ihn verlassen. Daher ist es nicht sinnvoll, einen direkten Bezug zwischen zwei Personen zu halten, die sich unterhalten.
  • Wir müssen noch die Kommunikation zwischen zwei Personen erleichtern und ihnen ein Gespräch ermöglichen.

Warum werden wir das bevorzugen mediator? Schauen Sie sich nur die Definition an:

Mit dem Mediatormuster wird die Kommunikation zwischen Objekten in einem Mediatorobjekt eingekapselt. Objekte kommunizieren nicht mehr direkt miteinander, sondern über den Mediator. Dies verringert die Abhängigkeiten zwischen kommunizierenden Objekten, wodurch die Kopplung verringert wird.

Wie funktioniert die Magie? Zuerst erstellen wir den Chat-Mediator und lassen die Personenobjekte registrieren, sodass er mit jeder einzelnen Person in zwei Richtungen verbunden ist (die Person kann mithilfe des Chat-Mediators eine Nachricht senden, da sie Zugriff darauf hat, und der Chat-Mediator greift darauf zu die empfangene Methode des Personenobjekts, weil er auch Zugriff darauf hat)

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

Beobachter

Wenn Sie die 911-Anrufanwendung erstellen, wählen Sie das observerEntwurfsmuster aus.

  • Jedes Krankenwagenobjekt observermöchte informiert werden, wenn ein Notfall vorliegt, damit er die Adresse fahren und Hilfe geben kann.
  • Der Notdienstmitarbeiter nimmt observableBezug auf jeden einzelnen Krankenwagen observersund benachrichtigt ihn, wenn Hilfe benötigt wird (oder ein Ereignis generiert wird).

Warum werden wir das bevorzugen observer? Schauen Sie sich nur die Definition an:

Ein Objekt, das als Subjekt bezeichnet wird, führt eine Liste seiner abhängigen Personen, die als Beobachter bezeichnet werden, und benachrichtigt sie automatisch über Statusänderungen, normalerweise durch Aufrufen einer ihrer Methoden.

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

Die Unterschiede:

  1. Der Chat mediatorverfügt über eine bidirektionale Kommunikation zwischen den Objekten der Person (Senden und Empfangen), wobei der Bediener observablenur über eine bidirektionale Kommunikation verfügt (er weist den Krankenwagen an, observerzu fahren und zu beenden).
  2. Der Chat mediatorkann dazu führen, dass die Objekte der Person zwischen ihnen interagieren (auch wenn es sich nicht um eine direkte Kommunikation handelt). Der Krankenwagen observersregistriert sich nur bei den Bedienerereignissen observable.
  3. Jedes Personenobjekt hat einen Verweis auf den Chat mediator, und auch der Chat enthält mediatoreinen Verweis auf jede einzelne Person. Während sich der Krankenwagen observernicht auf den Bediener bezieht, bezieht sich observablenur der Bediener observableauf jeden Krankenwagen observer.
Shahar Shokrani
quelle
3
Das letzte bisschen hilft. Mediator und Beobachter erreichen beide dasselbe Ziel, der Mediator ermöglicht jedoch eine bidirektionale Kommunikation, während der Beobachter nur in eine Richtung arbeitet.
Kiwicomb123
Genau, froh, dass es geholfen hat
Shahar Shokrani
6

Obwohl beide für die organisierte Darstellung von Zustandsänderungen verwendet werden, unterscheiden sie sich strukturell und semantisch geringfügig von IMO.

Observer wird verwendet, um eine Statusänderung eines bestimmten Objekts vom Objekt selbst zu übertragen. Die Änderung erfolgt also im zentralen Objekt, das auch für die Signalisierung verantwortlich ist. In Mediator kann die Statusänderung jedoch in jedem Objekt erfolgen, das jedoch von einem Mediator gesendet wird. Es gibt also einen Unterschied im Fluss. Ich glaube jedoch nicht, dass dies unser Codeverhalten beeinflusst. Wir können das eine oder andere verwenden, um das gleiche Verhalten zu erreichen. Andererseits kann dieser Unterschied einige Auswirkungen auf das konzeptionelle Verständnis des Codes haben.

Der Hauptzweck der Verwendung von Mustern besteht darin, eine gemeinsame Sprache zwischen Entwicklern zu erstellen. Wenn ich also einen Mediator sehe, verstehe ich persönlich mehrere Elemente, die versuchen, über einen einzelnen Broker / Hub zu kommunizieren, um Kommunikationsrauschen zu reduzieren (oder SRP zu fördern), und jedes Objekt ist gleichermaßen wichtig für die Fähigkeit, eine Zustandsänderung zu signalisieren. Stellen Sie sich zum Beispiel mehrere Flugzeuge vor, die sich einem Flughafen nähern. Jeder sollte über den Pylon (Mediator) kommunizieren, anstatt miteinander zu kommunizieren. (Denken Sie an 1000 Flugzeuge, die bei der Landung miteinander kommunizieren - das wäre ein Chaos.)

Wenn ich jedoch einen Beobachter sehe, bedeutet dies, dass es einige Statusänderungen gibt, die mir wichtig sein könnten, und ich sollte mich registrieren / abonnieren, um bestimmte Statusänderungen abzuhören. Es gibt ein zentrales Objekt, das für die Signalisierung von Statusänderungen verantwortlich ist. Wenn ich mich beispielsweise auf dem Weg von A nach B um einen bestimmten Flughafen kümmere, kann ich mich an diesem Flughafen registrieren, um einige Ereignisse zu verfolgen, die gesendet werden, beispielsweise wenn eine leere Landebahn oder ähnliches vorhanden ist.

Hoffe es ist klar.

stdout
quelle
5

@cdc erklärte den Unterschied in der Absicht ausgezeichnet.

Ich werde noch ein paar Infos hinzufügen.

Beobachter : Aktiviert die Benachrichtigung eines Ereignisses in einem Objekt an verschiedene Objektgruppen (Instanzen verschiedener Klassen).

Mediator : Zentralisieren Sie die Kommunikation zwischen Objekten, die aus einer bestimmten Klasse erstellt wurden.

Struktur des Mediatormusters aus der Dofabrik :

Geben Sie hier die Bildbeschreibung ein

Mediator : Definiert eine Schnittstelle für die Kommunikation zwischen Kollegen.

Kollege : Ist eine abstrakte Klasse, die die Ereignisse definiert, die zwischen Kollegen kommuniziert werden sollen

ConcreteMediator : Implementiert kooperatives Verhalten durch Koordination von Kollegenobjekten und Pflege seiner Kollegen

ConcreteColleague : Implementiert die über Mediator empfangenen Benachrichtigungsvorgänge , die von anderen Kollegen generiert wurden

Ein Beispiel aus der Praxis:

Sie verwalten ein Computernetzwerk in Mesh- Topologie. Wenn ein neuer Computer hinzugefügt oder ein vorhandener Computer entfernt wird, sollten alle anderen Computer in diesem Netzwerk über diese beiden Ereignisse informiert sein.

Mal sehen, wie das Mediator-Muster hineinpasst.

Code-Auszug:

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

Ausgabe:

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

Erläuterung:

  1. Eagle wird zunächst durch ein Registerereignis zum Netzwerk hinzugefügt. Keine Benachrichtigungen an andere Kollegen, da Eagle der erste ist.
  2. Wenn Strauß zum Netzwerk hinzugefügt wird, wird Eagle benachrichtigt: Zeile 1 der Ausgabe wird jetzt gerendert.
  3. Wenn Pinguin zum Netzwerk hinzugefügt wird, wurden sowohl Eagle als auch Ostrich benachrichtigt: Zeile 2 und Zeile 3 der Ausgabe werden jetzt gerendert.
  4. Als Eagle das Netzwerk aufgrund eines nicht registrierten Ereignisses verließ, wurden sowohl Strauß als auch Pinguin benachrichtigt. Zeile 4 und Zeile 5 der Ausgabe werden jetzt gerendert.
Ravindra Babu
quelle
2

Wie wäre es mit dieser Erklärung? Technisch gesehen sind sowohl Observer als auch Mediator gleich und werden verwendet, um eine entkoppelte Möglichkeit für die Komponentenkommunikation bereitzustellen, aber die Verwendung ist unterschiedlich.

Während abonnierte Komponenten über obeserver Statusänderungen informiert werden (z. B. Erstellung eines neuen Datenbankdatensatzes), registrierten die mediator Befehle Komponenten, um etwas im Zusammenhang mit dem Geschäftslogikfluss zu tun (Senden einer E-Mail an den Benutzer zum Zurücksetzen des Kennworts).

Beobachter

  • Benachrichtigungsverbraucher sind verpflichtet, sich anzumelden, um Benachrichtigungen zu erhalten
  • Die Benachrichtigungsverarbeitung ist nicht Teil des Geschäftsflusses

Vermittler

  • Explizite Registrierung erforderlich, um "Herausgeber" und "Verbraucher" zu verbinden
  • Die Benachrichtigungsverarbeitung ist Teil eines bestimmten Geschäftsablaufs
Saturn Technologies
quelle