web-dev-qa-db-fra.com

Modèles de conception orientés objet de Mediator Vs Observer

Je lisais le Gang Of Four, afin de résoudre certains de mes problèmes et suis tombé sur le modèle Mediator.

J'avais précédemment utilisé Observer dans mes projets pour créer une application graphique. Je suis un peu confus car je ne trouve pas de grande différence entre les deux. J'ai parcouru pour trouver la différence, mais je n'ai trouvé aucune réponse appropriée à ma requête.

Quelqu'un pourrait-il m'aider à différencier les deux avec un bon exemple qui les démarque clairement?

81
Fooo

Le modèle Observer: Définit une dépendance un à plusieurs entre les objets de sorte que lorsqu'un objet change d'état, tous ses dépendants soient notifiés et mis à jour automatiquement. 

Le modèle Mediator: Définit un objet qui encapsule la manière dont un ensemble d'objets interagit. Le médiateur favorise le couplage lâche en empêchant les objets de se référer explicitement les uns aux autres, et vous permet de faire varier leur interaction indépendamment. 

Source: dofactory

Exemple:

Le modèle d'observateur: La classe A peut avoir zéro ou plusieurs observateurs de type O enregistrés avec elle. Lorsqu'un élément de A est modifié, il en informe tous les observateurs.

Le modèle de médiateur: Vous avez un certain nombre d’occurrences de la classe X (ou peut-être même plusieurs types différents: X, Y et Z), et ils souhaitent communiquer entre eux (mais vous ne voulez pas que chacun ait une relation explicite). vous créez une classe de médiateurs M. Chaque instance de X a une référence à une instance partagée de M, à travers laquelle elle peut communiquer avec les autres instances de X (ou X, Y et Z).

94
cdc

Dans le livre original qui a inventé les termes Observateur et Médiateur, Modèles de conception, éléments d'un logiciel orienté objet réutilisable , il est indiqué que le modèle Médiateur peut être implémenté à l'aide du modèle observateur. Toutefois, il peut également être implémenté en faisant en sorte que Collègues (qui est à peu près équivalent au motif Sujets du observateur) ait une référence à une classe Mediator ou à une interface Mediator.

Il existe de nombreux cas dans lesquels vous souhaitez utiliser le modèle d'observateur. En général, un objet ne doit pas savoir quels autres objets observent son état.

Le médiateur est un peu plus spécifique, cela évite que les classes communiquent directement mais plutôt par le biais d'un médiateur. Cela contribue au principe de responsabilité unique en permettant de transférer la communication à une classe qui ne gère que la communication.

Un exemple classique de Mediator est une interface utilisateur graphique, où l'approche naïve peut conduire à coder un événement de clic sur un bouton en indiquant "si le panneau Foo est désactivé et que le panneau à barres comporte une étiquette indiquant" Veuillez entrer la date ", n'appelez pas le serveur, sinon, allez-y ", où avec le motif Médiateur, on pourrait dire" Je ne suis qu'un bouton et je n'ai aucune entreprise terrestre à connaître le panneau Foo et l'étiquette sur le panneau Bar, alors je demanderai simplement à mon médiateur d'appeler le serveur est OK en ce moment. "

Ou, si Mediator est implémenté à l'aide du motif Observer, le bouton indique "Hé, observateurs (qui incluent le médiateur), mon état a changé (quelqu'un m'a cliqué). Faites quelque chose si cela vous intéresse". Dans mon exemple, cela a probablement moins de sens que de faire directement référence au médiateur, mais dans de nombreux cas, utiliser le modèle Observer pour implémenter Mediator aurait du sens, et la différence entre Observer et Mediator serait davantage une intention que une différence dans le code lui-même.

31
psr

Observateur

1. sans

  • Client1 : Hey Sujet , quand changez-vous?

  • Client2 : Quand avez-vous changé Sujet ? Je n'ai pas remarqué!

  • Client3 : Je sais que Sujet a changé.

2. avec

  • Les clients sont silencieux.
  • Un peu plus tard ...
  • Sujet : Cher clients , j'ai changé!

Médiateur

1. sans

  • Client1 : Hé Taxi1 , emmène-moi un peu où.
  • Client2 : Hey Taxi1 , emmène-moi un peu où.
  • Client1 : Hé Taxi2 , emmène-moi un peu où.
  • Client2 : Hey Taxi2 , emmène-moi un peu où.

2. avec

  • Client1 : Hey TaxiCenter , veuillez me prendre un Taxi .
  • Client2 : Hey TaxiCenter , veuillez me prendre un Taxi .
29
Trix

Ces modèles sont utilisés dans différentes situations: 

Le modèle de médiateur est utilisé lorsque vous avez deux sous-systèmes avec une dépendance et que l’un d’eux doit changer, et comme vous ne souhaitez pas modifier le système qui dépend de l’autre, vous pouvez introduire un médiateur découpler la dépendance entre eux. Ainsi, lorsque l’un des sous-systèmes change, il vous suffit de mettre à jour le médiateur.

Le modèle d'observateur est utilisé lorsqu'une classe souhaite permettre à d'autres classes de s'enregistrer et de recevoir des notifications sur des événements, e. g. ButtonListener etc.

Ces deux modèles permettent un couplage moindre, mais sont très différents.

13
uzilan

Bien que les deux servent à décrire de manière organisée les changements d’état, ils sont légèrement différents sur le plan structurel et sémantique de l’OMI. 

Observer est utilisé pour diffuser un changement d'état d'un objet particulier, à partir de l'objet lui-même. Donc, le changement se produit dans l'objet central qui est également responsable de le signaler. Cependant, dans Mediator, un changement d'état peut se produire dans n'importe quel objet, mais il est diffusé à partir d'un médiateur. Donc, il y a une différence dans le flux. Mais, je ne pense pas que cela affecte notre comportement du code. Nous pouvons utiliser l'un ou l'autre pour obtenir le même comportement. D'autre part, cette différence pourrait avoir des effets sur la compréhension conceptuelle du code. 

Vous voyez, le but principal des patterns est plutôt de créer un langage commun entre développeurs. Ainsi, lorsque je vois un médiateur, je comprends personnellement plusieurs éléments qui tentent de communiquer sur un seul courtier/concentrateur afin de réduire le bruit de communication (ou de promouvoir la SRP). Chaque objet est également important pour pouvoir signaler un changement d'état. Par exemple, imaginez plusieurs aéronefs en approche d'un aéroport. Chacun doit communiquer sur le pylône (médiateur) plutôt que de communiquer entre eux. (Pensez à 1000 avions communiquant les uns avec les autres lors de l'atterrissage)

Cependant, quand je vois un observateur, cela signifie que certains changements d'état peuvent m'intéresser et que vous devez vous inscrire/vous abonner pour écouter les changements d'état particuliers. Il existe un objet central responsable de la signalisation des changements d'état. Par exemple, si je me soucie d'un aéroport spécifique entre A et B, je peux m'inscrire à cet aéroport pour suivre certains événements retransmis comme s'il y avait une piste vide ou quelque chose du genre. 

J'espère que c'est clair.

4
stdout

@cdc a très bien expliqué la différence d'intention. 

Je vais ajouter quelques informations supplémentaires sur le dessus.

Observer : Active la notification d'un événement dans un objet à différents ensembles d'objets (instances de différentes classes).

Mediator : Centraliser la communication entre un ensemble d'objets créé à partir d'une classe particulière. 

Structure du motif de médiateur de dofactory :

 enter image description here

Mediator: définit une interface pour la communication entre collègues.

Colleague: est une classe abstraite, qui définit les événements à communiquer entre collègues.

ConcreteMediator: met en œuvre un comportement coopératif en coordonnant les collègues objets et entretient ses collègues.

ConcreteColleague: implémente les opérations de notification reçues via Mediator, générées par d'autres Colleague

Un exemple concret:

Vous gérez un réseau d’ordinateurs dans la topologie Mesh. Si un nouvel ordinateur est ajouté ou qu'un ordinateur existant est supprimé, tous les autres ordinateurs de ce réseau doivent être informés de ces deux événements. 

Voyons comment le motif Mediator s'y intègre.

Extrait de code:

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

sortie:

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

Explication:

  1. Eagle est ajouté au réseau dans un premier temps via l'événement register. Aucune notification à aucun autre collègue puisque Eagle est le premier. 
  2. Lorsque Ostrich _ est ajouté au réseau, Eagle_ est averti: la ligne 1 de la sortie est maintenant affichée.
  3. Lorsque Penguin est ajouté au réseau, Eagle et Autruche ont été notifiés: les lignes 2 et 3 de la sortie sont maintenant affichées.
  4. Lorsque Eagle a quitté le réseau après un événement d'annulation d'enregistrement, les deux Autruche et Pingouin ont été notifiés. La ligne 4 et la ligne 5 de la sortie sont maintenant rendues.
4
Ravindra babu

Prenons un exemple: considérons que vous voulez créer deux applications:

  1. Application de chat.
  2. Application d'opérateur d'ambulance d'urgence.

médiateur

En créant l’application de discussion en ligne, vous choisissez le modèle mediator.

  • Les personnes peuvent être en train de rejoindre et de quitter le chat à tout moment, il n’a donc aucun sens de garder une référence directe entre deux personnes en train de bavarder.
  • Nous devons encore faciliter la communication entre deux personnes et leur permettre de discuter. 

Pourquoi préférons-nous la mediator? jetez un coup d’œil à sa définition:

Avec le motif médiateur, la communication entre les objets est encapsulé dans un objet médiateur. Les objets ne communiquent plus directement les uns avec les autres, mais au lieu de communiquer à travers le médiateur. Cela réduit les dépendances entre les objets en communication, réduisant ainsi le couplage.

Comment fonctionne la magie? Nous allons d’abord créer le médiateur de conversation et faire en sorte que les objets de personnes s’y inscrivent, afin d’avoir une connexion bidirectionnelle avec chaque personne (la personne peut envoyer un message à l’aide du médiateur de discussion car elle y a accès), la méthode reçue de l'objet personne car il y a également accès)

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

observateur

Lors de la création de l’appel 911, vous choisirez le modèle observer.

  • Chaque ambulance observer objet souhaite être informé en cas d'état d'urgence afin de pouvoir gérer l'adresse et apporter son aide. 
  • L’opérateur d’urgence observable garde une référence sur chaque ambulance observers et les informe quand de l’aide est nécessaire (ou lors de l’événement générateur).

Pourquoi préférons-nous la observer? jetez un coup d’œil à sa définition:

Un objet, appelé le sujet, maintient une liste de ses dépendants, appelé observateurs, et les informe automatiquement de tout état changements, généralement en appelant l’une de leurs méthodes.

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

Les différences:

  1. Le chat mediator a une communication bidirectionnelle entre les objets personnes (envoi et réception), l'opérateur observable n'ayant qu'une communication à sens unique (il indique à l'ambulance observer de conduire et de terminer).
  2. Le chat mediator peut faire interagir les objets personnes entre eux (même s'il ne s'agit pas d'une communication directe), les ambulances observers n'enregistrent que les événements opérateur observable.
  3. Chaque objet personne a une référence au chat mediator, et le chat mediator conserve également une référence à chacune des personnes. Lorsque l'ambulance observer ne conserve pas la référence à l'opérateur observable, seul l'opérateur observable conserve une référence pour chaque ambulance observer.
1
Shahar Shokrani

Comment à propos de cette explication Techniquement, Observer et Mediator sont identiques et sont utilisés pour fournir un moyen découplé de la communication entre composants, mais leur utilisation est différente.

Alors que obeservernotifiecomposants abonnés concernant les modifications d'état (création d'un nouvel enregistrement de base de données, par exemple), les composants mediatorcommandesenregistrés permettent d'effectuer des opérations liées aux activités flux logique (envoi d'un courrier électronique à l'utilisateur pour réinitialisation du mot de passe).

Observateur

  • Les consommateurs de notifications sont responsables de s'abonner afin de recevoir des notifications
  • Le traitement des notifications ne fait pas partie des flux commerciaux

Médiateur

  • Enregistrement explicite requis pour connecter "éditeur" et "consommateurs"
  • Le traitement des notifications fait partie d'un flux commercial spécifique
0