Wzorce projektowe zorientowane obiektowo Mediator Vs Observer

95

Czytałem Gang Of Four , aby rozwiązać niektóre z moich problemów i natrafiłem na wzorzec Mediatora .

Wcześniej korzystałem z Observera w moich projektach do tworzenia aplikacji GUI. Jestem trochę zdezorientowany, ponieważ nie widzę między nimi dużej różnicy. Przeglądałem, aby znaleźć różnicę, ale nie mogłem znaleźć trafnej odpowiedzi na moje zapytanie.

Czy ktoś mógłby mi pomóc w rozróżnieniu między nimi za pomocą jakiegoś dobrego przykładu, który wyraźnie je rozgranicza?

Fooo
źródło
5
Moja prośba o migrację tego pytania do Programmers.StackExchangezostała odrzucona, ale zamieściłem tam podobny post , ponieważ byłem zainteresowany odpowiedzią. Niektóre odpowiedzi mogą być interesujące. :)
Rachel,
Jeśli chodzi o przykłady JavaScript, możesz spojrzeć na moją odpowiedź na podobne pytanie .
Alex Pakka,
Oryginalna książka GoF odnosi się do tego w punkcie # 8 dotyczącym implementacji, podając przykład a ChangeManagerdla Observerwzorca, który używa Mediator. widzieć; paginas.fe.up.pt/~aaguiar/as/gof/hires/pat5g.htm#samplecode
robi-y

Odpowiedzi:

106

Wzorzec obserwatora: definiuje zależność jeden-do-wielu między obiektami, dzięki czemu gdy jeden obiekt zmieni stan, wszystkie jego elementy zależne są powiadamiane i aktualizowane automatycznie.

Wzorzec Mediator: Zdefiniuj obiekt, który zawiera sposób interakcji zestawu obiektów. Mediator promuje luźne powiązania, uniemożliwiając obiektom bezpośrednie odwoływanie się do siebie, i pozwala niezależnie zmieniać ich interakcje.

Źródło: dofactory

Przykład:

Wzorzec obserwatora: Klasa A, może mieć zarejestrowanych zero lub więcej obserwatorów typu O. Kiedy coś w A zostanie zmienione, powiadamia to wszystkich obserwatorów.

Wzorzec mediatora: masz pewną liczbę wystąpień klasy X (lub może nawet kilka różnych typów: X, Y i Z) i oni chcą się ze sobą komunikować (ale nie chcesz, aby każda z nich miała wyraźne odniesienia do każdego z nich other), więc tworzysz klasę mediatora M. Każda instancja X ma odniesienie do współdzielonej instancji M, za pośrednictwem której może komunikować się z innymi instancjami X (lub X, Y i Z).

CDC
źródło
wyjaśnienie obserwatora wydaje się być zbliżone do wzorca poleceń, a nie wzorca obserwatora
Aun,
41

W oryginalnej książce, która ukuła terminy Obserwator i Mediator, Wzorce projektowe, Elementy oprogramowania obiektowego wielokrotnego użytku , mówi, że wzorzec Mediatora można zaimplementować za pomocą wzorca obserwatora. Jednak można to również zaimplementować, mając Koledzy (którzy są z grubsza równoważne z Podmiotami wzorca Obserwatora), mając odniesienie do klasy Mediator lub interfejsu Mediator.

Jest wiele przypadków, w których chciałbyś użyć wzorca obserwatora, kluczem jest to, że obiekt nie powinien wiedzieć, jakie inne obiekty obserwują jego stan.

Mediator jest nieco bardziej szczegółowy, unika sytuacji, w której klasy komunikują się bezpośrednio, ale zamiast tego za pośrednictwem mediatora. Pomaga to zasadzie pojedynczej odpowiedzialności, umożliwiając przekazanie komunikacji do klasy, która po prostu zajmuje się komunikacją.

Klasyczny przykład Mediatora jest w GUI, gdzie naiwne podejście może prowadzić do kodu po kliknięciu przycisku mówiącego „jeśli panel Foo jest wyłączony, a panel Bar ma etykietę z napisem„ Proszę wprowadzić datę ”, to nie dzwoń do serwera, inaczej idź dalej ”, gdzie w przypadku wzorca Mediator mógłby powiedzieć„ Jestem tylko przyciskiem i nie mam nic ziemskiego, wiedząc o panelu Foo i etykiecie na panelu Bar, więc po prostu zapytam mojego mediatora, czy dzwonię do serwera jest teraz w porządku ”.

Lub, jeśli Mediator jest zaimplementowany przy użyciu wzorca Observer, przycisk będzie mówił „Hej, obserwatorzy (w tym mediator), mój stan się zmienił (ktoś mnie kliknął). Zrób coś z tym, jeśli ci zależy”. W moim przykładzie to prawdopodobnie ma mniej sensu niż bezpośrednie odwoływanie się do mediatora, ale w wielu przypadkach użycie wzorca Observer do implementacji Mediatora miałoby sens, a różnica między Observer i Mediator byłaby bardziej intencjonalna niż różnica w samym kodzie.

psr
źródło
Szukałem tego słowa „zasada pojedynczej odpowiedzialności”.
wyjście
37

Obserwator

1. Bez

  • Klient1 : Hej Temat , kiedy się zmieniasz?

  • Klient2 : Kiedy zmieniłeś Temat ? Nie zauważyłem!

  • Klient3 : Wiem, że Temat się zmienił.

2. Z

  • Klienci milczą.
  • Jakiś czas później ...
  • Temat : Drodzy Klienci , zmieniłem się!

Mediator

1. Bez

  • Klient1 : Hej Taxi1 , zabierz mnie gdzieś.
  • Klient2 : Hej Taxi1 , zabierz mnie gdzieś.
  • Klient1 : Hej Taxi2 , zabierz mnie gdzieś.
  • Klient2 : Hej Taxi2 , zabierz mnie gdzieś.

2. Z

  • Klient1 : Hej TaxiCenter , weź taksówkę .
  • Klient2 : Hej TaxiCenter , weź taksówkę .
Pmpr
źródło
2
Twój przykład mediatora jest wzorcem fabrycznym, a nie wzorcem mediatora
Mohammad Karimi,
2
@Pmpr To jest wzorzec projektowy mediatora. TaxiCenter nie tworzy taksówek, tylko udostępnia je w jakiś sposób (prawdopodobnie każda taksówka czeka, aż TaxiCenter powie, że to twoja kolej)
Siva R
14

Te wzorce są używane w różnych sytuacjach:

Wzorzec mediatora jest używany, gdy masz dwa podsystemy z pewnymi zależnościami, a jeden z nich wymaga zmiany, a ponieważ możesz nie chcieć zmieniać systemu zależnego od drugiego, możesz chcieć wprowadzić mediatora, który rozdzielić zależności między nimi. W ten sposób, gdy jeden z podsystemów ulegnie zmianie, wszystko, co musisz zrobić, to zaktualizować mediatora.

Wzorzec obserwatora jest używany, gdy klasa chce zezwolić innym klasom na rejestrację i otrzymywanie powiadomień o zdarzeniach, np. ButtonListener itp.

Oba te wzorce pozwalają na mniejsze sprzężenie, ale są zupełnie inne.

uzilan
źródło
7

Weźmy przykład: rozważmy, że chcesz zbudować dwie aplikacje:

  1. Aplikacja do czatu.
  2. Aplikacja operatora karetki pogotowia ratunkowego.

mediator

Budując aplikację do czatu, wybierzesz mediatorwzorzec projektowy.

  • Osoby mogą dołączać do czatu i opuszczać go w dowolnym momencie, więc nie ma sensu utrzymywanie bezpośredniego odniesienia między dwiema rozmawiającymi osobami.
  • Nadal musimy ułatwić komunikację między dwiema osobami i pozwolić im na rozmowę.

Dlaczego wolimy mediator? wystarczy spojrzeć na jego definicję:

W przypadku wzorca mediatora komunikacja między obiektami jest zamknięta w obiekcie mediatora. Obiekty nie komunikują się już bezpośrednio ze sobą, ale zamiast tego komunikują się przez mediatora. Zmniejsza to zależności między komunikującymi się obiektami, zmniejszając w ten sposób sprzężenie.

Jak działa magia? Najpierw stworzymy mediatora czatu i sprawimy, że obiekty osób będą się do niego rejestrować, aby miał dwukierunkowe połączenie z każdą osobą (osoba może wysłać wiadomość za pomocą mediatora czatu, ponieważ ma do niej dostęp, a mediator czatu będzie miał do niej dostęp otrzymana metoda przedmiotu osoby, ponieważ on również ma do niej dostęp)

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

obserwator

Budując aplikację wzywania 911, będziesz wybierać observerwzorzec projektowy.

  • Każdy observerobiekt karetki chce być informowany o sytuacji awaryjnej, aby mógł podjechać pod adres i udzielić pomocy.
  • Operator ratunkowy observableutrzymuje łączność z każdym w karetce observersi powiadamia ich, gdy potrzebna jest pomoc (lub zdarzenie generujące).

Dlaczego wolimy observer? wystarczy spojrzeć na jego definicję:

Obiekt, zwany podmiotem, przechowuje listę swoich elementów zależnych, zwanych obserwatorami, i powiadamia ich automatycznie o wszelkich zmianach stanu, zwykle przez wywołanie jednej z ich metod.

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

Różnice:

  1. Czat mediatorma dwukierunkową komunikację między obiektami osób (wysyłanie i odbieranie), podczas gdy operator observablema tylko jednokierunkową komunikację (nakazuje karetce observerjechać i kończyć).
  2. Czat mediatormoże sprawić, że obiekty osób wchodzą w interakcję (nawet jeśli nie jest to komunikacja bezpośrednia), karetki observersrejestrują tylko observablezdarzenia operatora .
  3. Każdy obiekt osoby ma odniesienie do czatu mediator, a także czat mediatorzachowuje odniesienie do każdej z osób. Gdzie karetka observernie ma odniesienia do operatora observable, tylko operator observablema odniesienie do każdej karetki observer.
Shahar Shokrani
źródło
3
Ostatni kawałek pomaga. Zarówno Mediator, jak i Observer osiągają ten sam cel, jednak mediator umożliwia komunikację dwukierunkową, podczas gdy obserwator działa tylko w jedną stronę.
kiwicomb123
Dokładnie, cieszę się, że pomogło
Shahar Shokrani
Kiedy przeczytałem nagłówek, miałem dokładnie odwrotny sposób, w rzeczywistości call center pogotowia jest mediatorem - który odbiera telefon, a następnie wysyła karetkę, policję i strażaków A każda karetka w radiostacji tak jest obserwatorem - czekają na wydarzenie w radiu to ich dotyka Na czacie: serwer jest mediatorem, każdy klient - obserwator Brzmi dobrze według Ciebie?
Maks
Hej @Maks, myśl o karetce jako zautomatyzowanej, nie kontaktują się ze swoim operatorem przez radio, tylko sprawdzają lokalizację i jeżdżą.
Shahar Shokrani
7

Chociaż oba są używane do zorganizowanego sposobu informowania o zmianach stanu, różnią się nieco strukturalnie i semantycznie IMO.

Observer jest używany do rozgłaszania zmiany stanu określonego obiektu z samego obiektu. Zatem zmiana zachodzi w centralnym obiekcie, który jest również odpowiedzialny za jej sygnalizację. Jednak w Mediatorze zmiana stanu może nastąpić w dowolnym obiekcie, ale jest transmitowana przez mediatora. Więc jest różnica w przepływie. Ale nie sądzę, że ma to wpływ na zachowanie naszego kodu. Możemy użyć jednego lub drugiego, aby osiągnąć to samo zachowanie. Z drugiej strony ta różnica może mieć pewien wpływ na koncepcyjne rozumienie kodu.

Widzisz, głównym celem używania wzorców jest raczej stworzenie wspólnego języka między programistami. Tak więc, kiedy widzę mediatora, osobiście rozumiem wiele elementów próbujących komunikować się za pośrednictwem jednego brokera / koncentratora, aby zmniejszyć szum komunikacji (lub promować SRP), a każdy obiekt jest równie ważny pod względem zdolności sygnalizowania zmiany stanu. Na przykład pomyśl o wielu samolotach zbliżających się do lotniska. Każdy powinien komunikować się przez pylon (mediatora), zamiast komunikować się ze sobą. (Pomyśl o 1000 samolotach komunikujących się ze sobą podczas lądowania - to byłby bałagan)

Jednak kiedy widzę obserwatora, oznacza to, że są pewne zmiany stanu, które mogą mnie interesować i powinienem zarejestrować / zasubskrybować, aby słuchać poszczególnych zmian stanu. Istnieje centralny obiekt odpowiedzialny za sygnalizację zmian stanu. Na przykład, jeśli zależy mi na konkretnym lotnisku w drodze z punktu A do B, mogę zarejestrować się na to lotnisko, aby obejrzeć transmitowane wydarzenia, na przykład pusty pas startowy lub coś w tym rodzaju.

Mam nadzieję, że to jasne.

stdout
źródło
5

@cdc doskonale wyjaśnił różnicę w zamiarze.

Dodam więcej informacji na górze.

Obserwator : umożliwia powiadomienie o zdarzeniu w jednym obiekcie do innego zestawu obiektów (instancje różnych klas)

Mediator : scentralizuj komunikację między zbiorem obiektów utworzonych z określonej klasy.

Struktura wzorca Mediator z dofactory :

wprowadź opis obrazu tutaj

Mediator : definiuje interfejs do komunikacji między współpracownikami.

Kolega : to klasa abstrakcyjna, która definiuje wydarzenia, które mają być przekazywane między kolegami

ConcreteMediator : wdraża zachowanie kooperacyjne poprzez koordynację obiektów Colleague i opiekę nad współpracownikami

ConcreteColleague : implementuje operacje zawiadomieniem otrzymanym przez Pośrednika , który został wygenerowany przez innego współpracownika

Jeden przykład z prawdziwego świata:

Utrzymujesz sieć komputerów w topologii Mesh . Jeśli zostanie dodany nowy komputer lub istniejący komputer zostanie usunięty, wszystkie inne komputery w tej sieci powinny wiedzieć o tych dwóch zdarzeniach.

Zobaczmy, jak pasuje do niego wzorzec Mediatora.

Fragment kodu:

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

wynik:

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

Wyjaśnienie:

  1. Eagle jest dodawany do sieci najpierw poprzez rejestrację. Brak powiadomień dla innych kolegów, ponieważ Eagle jest pierwszym.
  2. Gdy Struś jest dodawany do sieci, Eagle jest powiadamiany: Linia 1 wyjścia jest teraz renderowana.
  3. Kiedy Penguin jest dodawany do sieci, zarówno orzeł , jak i struś są powiadamiane: linia 2 i linia 3 wyjścia są teraz renderowane.
  4. Kiedy Eagle opuścił sieć przez zdarzenie wyrejestrowania, zarówno Struś , jak i Pingwin zostali powiadomieni. Linia 4 i Linia 5 wyjścia są teraz renderowane.
Ravindra babu
źródło
2

Jak O tym wyjaśnieniu Technicznie zarówno Observer, jak i Mediator są takie same i służą do zapewnienia oddzielonego sposobu komunikacji komponentów, ale użycie jest inne.

Podczas obeserver powiadamiania subskrybowanych komponentów o zmianach stanu (na przykład utworzenie nowego rekordu bazy danych), mediator komendy zarejestrowały komponenty w celu zrobienia czegoś związanego z przepływem logiki biznesowej (wysłanie wiadomości e-mail do użytkownika w celu zresetowania hasła).

Obserwator

  • Konsumenci powiadomień są odpowiedzialni za zapisanie się, aby otrzymywać powiadomienia
  • Przetwarzanie powiadomień nie jest częścią przepływu biznesowego

Mediator

  • Do połączenia „wydawcy” i „konsumentów” wymagana jest jawna rejestracja
  • Przetwarzanie powiadomień jest częścią określonego przepływu biznesowego
Saturn Technologies
źródło