Mediator Vs Observer Design Patterns orientati agli oggetti


93

Ho letto Gang Of Four , al fine di risolvere alcuni dei miei problemi e mi sono imbattuto nel modello Mediator .

In precedenza avevo utilizzato Observer nei miei progetti per creare alcune applicazioni GUI. Sono un po 'confuso perché non trovo grandi differenze tra i due. Ho esplorato per trovare la differenza ma non sono riuscito a trovare alcuna risposta adatta alla mia domanda.

Qualcuno potrebbe aiutarmi a distinguere tra i due con qualche buon esempio che demarca chiaramente i due?


5
La mia richiesta di migrare questa domanda è Programmers.StackExchangestata respinta, ma ho scritto un post simile perché ero interessato alla risposta. Potresti trovare interessanti alcune delle risposte. :)
Rachel

Per esempi JavaScript, puoi dare un'occhiata alla mia risposta a una domanda simile .
Alex Pakka

Il libro GoF originale affronta il punto 8 della sezione di implementazione, fornendo un esempio di a ChangeManagerper il Observermodello che utilizza Mediator. vedere; paginas.fe.up.pt/~aaguiar/as/gof/hires/pat5g.htm#samplecode
robi-y

Risposte:


104

Il modello Observer: definisce una dipendenza uno-a-molti tra gli oggetti in modo che quando un oggetto cambia stato, tutti i suoi dipendenti vengono avvisati e aggiornati automaticamente.

Il modello Mediator: definisci un oggetto che incapsula il modo in cui un insieme di oggetti interagisce. Mediator promuove l'accoppiamento libero impedendo agli oggetti di fare riferimento esplicitamente l'uno all'altro e ti consente di variare la loro interazione in modo indipendente.

Fonte: dofactory

Esempio:

Il pattern dell'osservatore: Classe A, può avere zero o più osservatori di tipo O registrati con esso. Quando qualcosa in A viene cambiato, avvisa tutti gli osservatori.

Il modello del mediatore: hai un certo numero di istanze della classe X (o forse anche diversi tipi diversi: X, Y e Z), e loro desiderano comunicare tra loro (ma non vuoi che ciascuna abbia riferimenti espliciti a ciascuna altro), quindi crei una classe mediatore M. Ogni istanza di X ha un riferimento a un'istanza condivisa di M, attraverso la quale può comunicare con le altre istanze di X (o X, Y e Z).


la spiegazione dell'osservatore sembra essere vicina al modello di comando piuttosto che al modello dell'osservatore
Aun

40

Nel libro originale che ha coniato i termini Observer e Mediator, Design Patterns, Elements of Reusable Object-Oriented Software , si dice che il pattern Mediator può essere implementato utilizzando il pattern observer. Tuttavia, può anche essere implementato facendo in modo che i colleghi (che sono più o meno equivalenti ai soggetti del modello Observer) abbiano un riferimento a una classe Mediator oa un'interfaccia Mediator.

Ci sono molti casi in cui vorresti usare il pattern dell'osservatore, la chiave è che un oggetto non dovrebbe sapere quali altri oggetti stanno osservando il suo stato.

Il mediatore è un po 'più specifico, evita che le classi comunichino direttamente ma invece attraverso un mediatore. Ciò aiuta il principio di responsabilità unica consentendo di scaricare la comunicazione a una classe che gestisce solo la comunicazione.

Un classico esempio di Mediator è in una GUI, dove l'approccio ingenuo potrebbe portare a un codice su un evento di clic del pulsante che dice "se il pannello Foo è disabilitato e il pannello Bar ha un'etichetta che dice" Inserisci la data ", non chiamare il server, altrimenti vai avanti ", dove con il pattern Mediator potrebbe dire" Sono solo un pulsante e non ho affari terreni conoscendo il pannello Foo e l'etichetta sul pannello Bar, quindi chiederò al mio mediatore se chiama il server va bene adesso. "

Oppure, se Mediator è implementato utilizzando il pattern Observer, il pulsante direbbe "Ehi, osservatori (che includerebbe il mediatore), il mio stato è cambiato (qualcuno mi ha cliccato). Fai qualcosa al riguardo se ti interessa". Nel mio esempio questo probabilmente ha meno senso quindi fare riferimento direttamente al mediatore, ma in molti casi l'uso del pattern Observer per implementare Mediator avrebbe senso, e la differenza tra Observer e Mediator sarebbe più un'intenzione che una differenza nel codice stesso.


Cercavo questa parola "principio di responsabilità unica".
stdout

37

Osservatore

1. Senza

  • Cliente1 : Ehi Soggetto , quando cambi?

  • Cliente2 : Quando hai cambiato Oggetto ? Non ho notato!

  • Cliente3 : So che il Soggetto è cambiato.

2. Con

  • I clienti tacciono.
  • Qualche tempo dopo ...
  • Oggetto : Cari clienti , sono cambiato!

Mediatore

1. Senza

  • Cliente1 : Hey Taxi1 , portami da qualche parte.
  • Cliente2 : Hey Taxi1 , portami da qualche parte.
  • Cliente1 : Hey Taxi2 , portami da qualche parte.
  • Cliente2 : Hey Taxi2 , portami da qualche parte.

2. Con

  • Cliente1 : Hey TaxiCenter , per favore portami un taxi .
  • Cliente2 : Hey TaxiCenter , per favore portami un taxi .

2
Il tuo esempio di mediatore è il modello di fabbrica, non il modello di mediatore
Mohammad Karimi

2
@Pmpr È il modello di progettazione del mediatore. TaxiCenter non creerà taxi, rende i taxi disponibili in qualche modo (probabilmente ogni taxi attende fino a quando TaxiCenter dice che è il tuo turno)
Siva R

14

Questi modelli vengono utilizzati in diverse situazioni:

Il modello mediatore viene utilizzato quando si hanno due sottosistemi con una certa dipendenza e uno di essi deve essere modificato, e poiché potresti non voler cambiare il sistema che dipende dall'altro, potresti voler introdurre un mediatore che lo farà disaccoppia la dipendenza tra di loro. In questo modo, quando uno dei sottosistemi cambia, tutto ciò che devi fare è aggiornare il mediatore.

Il pattern observer viene utilizzato quando una classe vuole consentire ad altre classi di registrarsi e ricevere notifiche su eventi, ad esempio ButtonListener ecc.

Entrambi questi modelli consentono un accoppiamento minore, ma sono abbastanza diversi.


7

Facciamo un esempio: considera che vuoi creare due applicazioni:

  1. Applicazione di chat.
  2. Applicazione operatore ambulanza di emergenza.

mediatore

Costruendo l'applicazione di chat sceglierai il mediatormodello di progettazione.

  • Le persone possono entrare e uscire dalla chat in un dato momento, quindi non ha alcun senso mantenere un riferimento diretto tra due persone che chattano.
  • Dobbiamo ancora facilitare una comunicazione tra due persone e consentire loro di fare una chat.

Perché preferiremo il mediator? dai un'occhiata alla sua definizione:

Con il modello mediatore, la comunicazione tra gli oggetti è incapsulata all'interno di un oggetto mediatore. Gli oggetti non comunicano più direttamente tra loro, ma comunicano invece attraverso il mediatore. Ciò riduce le dipendenze tra gli oggetti in comunicazione, riducendo così l'accoppiamento.

Come funziona la magia? Per prima cosa creeremo il mediatore di chat e faremo in modo che gli oggetti delle persone si registrino ad esso, quindi avrà due connessioni direzionali con ogni singola persona (la persona può inviare un messaggio usando il mediatore di chat perché ha accesso ad esso, e il mediatore di chat accederà il metodo ricevuto dall'oggetto persona perché anche lui ha accesso ad esso)

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

osservatore

Costruendo l'applicazione di chiamata 911 sceglierai il observermodello di progettazione.

  • Ogni observeroggetto ambulanza desidera essere informato quando c'è uno stato di emergenza, in modo che possa guidare l'indirizzo e dare aiuto.
  • L'operatore del pronto soccorso observabletiene il riferimento a ciascuno dell'ambulanza observerse lo avvisa quando è necessario aiuto (o evento generatore).

Perché preferiremo il observer? dai un'occhiata alla sua definizione:

Un oggetto, chiamato soggetto, mantiene un elenco dei suoi dipendenti, chiamati osservatori, e li notifica automaticamente di qualsiasi cambiamento di stato, di solito chiamando uno dei loro metodi.

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

Le differenze:

  1. La chat mediatorha una comunicazione bidirezionale tra gli oggetti delle persone (invio e ricezione) mentre l'operatore observableha una comunicazione unidirezionale (dice all'ambulanza observerdi guidare e finire).
  2. La chat mediatorpuò far interagire le persone oggetti tra loro (anche se non è una comunicazione diretta), le ambulanze observerssi registrano solo agli observableeventi dell'operatore .
  3. Ogni oggetto persona ha un riferimento alla chat mediator, e anche la chat mediatormantiene il riferimento a ciascuna delle persone. Se l'ambulanza observernon tiene il riferimento all'operatore observable, solo l'operatore observablemantiene il riferimento a ogni ambulanza observer.

3
L'ultima parte aiuta. Il mediatore e l'osservatore raggiungono entrambi lo stesso obiettivo, tuttavia il mediatore consente la comunicazione bidirezionale mentre l'osservatore lavora solo in un modo.
kiwicomb123

Esatto, contento che abbia aiutato
Shahar Shokrani

6

Sebbene entrambi siano usati per un modo organizzato di raccontare i cambiamenti di stato, sono leggermente diversi strutturalmente e semanticamente secondo l'IMO.

Observer viene utilizzato per trasmettere un cambiamento di stato di un particolare oggetto, dall'oggetto stesso. Quindi il cambiamento avviene nell'oggetto centrale che è anche responsabile della sua segnalazione. Tuttavia, in Mediator, il cambiamento di stato può avvenire in qualsiasi oggetto ma viene trasmesso da un mediatore. Quindi c'è una differenza nel flusso. Ma non credo che questo influenzi il comportamento del nostro codice. Possiamo usarne uno o l'altro per ottenere lo stesso comportamento. D'altra parte, questa differenza potrebbe influire sulla comprensione concettuale del codice.

Vedi, lo scopo principale dell'uso dei pattern è piuttosto quello di creare un linguaggio comune tra gli sviluppatori. Quindi, quando vedo un mediatore, capisco personalmente più elementi che cercano di comunicare su un singolo broker / hub per ridurre il rumore di comunicazione (o per promuovere SRP) e ogni oggetto è altrettanto importante in termini di capacità di segnalare un cambiamento di stato. Ad esempio, pensa a più aeromobili che si avvicinano a un aeroporto. Ciascuno dovrebbe comunicare tramite il pilone (mediatore) piuttosto che comunicare tra loro. (Pensa a 1000 aeromobili che comunicano tra loro durante l'atterraggio: sarebbe un disastro)

Tuttavia, quando vedo un osservatore, significa che ci sono alcuni cambiamenti di stato che potrebbero interessarmi e che dovrei registrarmi / iscrivermi per ascoltare particolari cambiamenti di stato. C'è un oggetto centrale responsabile della segnalazione dei cambiamenti di stato. Ad esempio, se mi interessa un aeroporto specifico nel mio tragitto da A a B, posso registrarmi a quell'aeroporto per assistere ad alcuni eventi trasmessi come se ci fosse una pista vuota o qualcosa del genere.

Spero sia chiaro.


5

@cdc ha spiegato la differenza di intenti in modo eccellente.

Aggiungerò qualche informazione in più sopra.

Observer : abilita la notifica di un evento in un oggetto a diversi set di oggetti (istanze di classi diverse)

Mediatore : centralizza la comunicazione tra un insieme di oggetti, creato da una particolare classe.

Struttura del modello Mediator di dofactory :

inserisci qui la descrizione dell'immagine

Mediatore : definisce un'interfaccia per la comunicazione tra i colleghi.

Collega : è una classe astratta, che definisce gli eventi da comunicare tra i colleghi

ConcreteMediator : implementa un comportamento cooperativo coordinando gli oggetti dei colleghi e mantiene i suoi colleghi

ConcreteColleague : implementa le operazioni di notifica ricevute tramite Mediator , che sono state generate da un altro collega

Un esempio del mondo reale:

Stai mantenendo una rete di computer in topologia Mesh . Se viene aggiunto un nuovo computer o viene rimosso un computer esistente, tutti gli altri computer in quella rete dovrebbero essere a conoscenza di questi due eventi.

Vediamo come si inserisce il pattern Mediator.

Snippet di codice:

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

produzione:

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

Spiegazione:

  1. Eagle viene aggiunto alla rete inizialmente tramite un evento di registro. Nessuna notifica ad altri colleghi poiché Eagle è il primo.
  2. Quando Ostrich viene aggiunto alla rete, Eagle riceve una notifica: Ora viene eseguito il rendering della riga 1 dell'output.
  3. Quando Penguin viene aggiunto alla rete, sia Eagle che Ostrich sono stati informati: Ora viene eseguito il rendering della riga 2 e della riga 3 dell'output.
  4. Quando Eagle ha lasciato la rete a causa di un evento di annullamento della registrazione, sia Ostrich che Penguin sono stati informati. La riga 4 e la riga 5 dell'output sono ora rese.

2

Che ne dici di questa spiegazione Tecnicamente sia Observer che Mediator sono uguali e vengono utilizzati per fornire un modo disaccoppiato per la comunicazione dei componenti, ma l'utilizzo è diverso.

Mentre obeserver notifica ai componenti sottoscritti i cambiamenti di stato (creazione di un nuovo record db, ad esempio), i mediator comandi hanno registrato componenti per eseguire operazioni relative al flusso della logica aziendale (invio di e-mail all'utente per la reimpostazione della password).

Osservatore

  • I consumatori di notifiche sono tenuti a iscriversi per ricevere le notifiche
  • L'elaborazione delle notifiche non fa parte del flusso aziendale

Mediatore

  • Registrazione esplicita richiesta per collegare "editore" e "consumatori"
  • L'elaborazione delle notifiche fa parte di un flusso aziendale specifico
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.