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

92

J'ai lu 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 à faire la différence entre les deux avec un bon exemple qui délimite clairement les deux?

Fooo
la source
5
Ma demande de migration de cette question vers a Programmers.StackExchangeété refusée, mais j'ai publié un message similaire parce que j'étais intéressé par la réponse. Vous pourriez trouver certaines des réponses intéressantes. :)
Rachel
Pour des exemples JavaScript, vous pouvez consulter ma réponse à une question similaire .
Alex Pakka
Le livre original du GoF aborde cela sous le point n ° 8 de la section d'implémentation, en donnant un exemple d'un ChangeManagerpour le Observermodèle qui utilise Mediator. voir; paginas.fe.up.pt/~aaguiar/as/gof/hires/pat5g.htm#samplecode
robi-y

Réponses:

104

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 sont notifiés et mis à jour automatiquement.

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

Source: dofactory

Exemple:

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

Le modèle du médiateur: vous avez un certain nombre d'instances de 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 des références explicites à chacun other), vous créez donc une classe de médiateur 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).

CDC
la source
l'explication de l'observateur semble être plus proche du modèle de commande que du modèle de l'observateur
Aun
40

Dans le livre original qui a inventé les termes Observer et Mediator, Design Patterns, Elements of Reusable Object-Oriented Software , il est dit que le modèle Mediator peut être implémenté en utilisant le modèle d'observateur. Cependant, il peut également être implémenté en faisant en sorte que les collègues (qui sont à peu près équivalents aux sujets du modèle Observer) aient une référence à une classe Mediator ou à une interface Mediator.

Il existe de nombreux cas où vous souhaitez utiliser le modèle d'observateur, la clé est qu'un objet ne doit pas savoir quels autres objets observent son état.

Le médiateur est un peu plus précis, il évite que les classes communiquent directement mais plutôt par l'intermédiaire d'un médiateur. Cela facilite le principe de responsabilité unique en permettant à la communication d'être déchargée vers une classe qui gère simplement la communication.

Un exemple classique de Mediator est dans une interface graphique, où l'approche naïve peut conduire à un code sur un événement de clic de bouton disant "si le panneau Foo est désactivé et que le panneau Bar a une étiquette disant" Veuillez saisir la date ", n'appelez pas le serveur, sinon allez-y ", où avec le modèle Mediator, il pourrait dire" Je ne suis qu'un bouton et je n'ai aucune entreprise terrestre au courant du panneau Foo et de l'étiquette du panneau Bar, alors je vais simplement demander à mon médiateur s'il appelle le serveur est OK maintenant. "

Ou, si Mediator est implémenté en utilisant le modèle Observer, le bouton dirait "Hey, observateurs (ce qui inclurait le médiateur), mon état a changé (quelqu'un m'a cliqué). Faites quelque chose si vous vous en souciez". Dans mon exemple, cela a probablement moins de sens que de faire référence directement au médiateur, mais dans de nombreux cas, l'utilisation du modèle Observer pour implémenter Mediator aurait du sens, et la différence entre Observer et Mediator serait plus une différence d'intention qu'une différence dans le code lui-même.

psr
la source
Je cherchais ce mot "principe de responsabilité unique".
stdout
37

Observateur

1. Sans

  • Client1 : Hey Subject , quand changez-vous?

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

  • Client3 : Je sais que le sujet a changé.

2. Avec

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

Médiateur

1. Sans

  • Client1 : Hey Taxi1 , emmène-moi quelque part.
  • Client2 : Hey Taxi1 , emmène-moi quelque part.
  • Client1 : Hey Taxi2 , emmène-moi quelque part.
  • Client2 : Hey Taxi2 , emmène-moi quelque part.

2. Avec

  • Client1 : Hey TaxiCenter , s'il vous plaît, prenez-moi un taxi .
  • Client2 : Hey TaxiCenter , s'il vous plaît, prenez-moi un taxi .
Pmpr
la source
2
Votre exemple de médiateur est un modèle d'usine, pas un modèle de médiateur
Mohammad Karimi
2
@Pmpr C'est un modèle de conception de médiateur. TaxiCenter ne créera pas de taxis, il les rend disponibles par certains moyens (probablement chaque taxi attend que TaxiCenter dise que c'est votre tour)
Siva R
14

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 certaine dépendance et que l'un d'entre eux doit changer, et comme vous ne voudrez peut-être pas changer le système qui dépend de l'autre, vous voudrez peut-être introduire un médiateur qui découpler la dépendance entre eux. De cette façon, lorsque l'un des sous-systèmes change, tout ce que vous avez à faire est de mettre à jour le médiateur.

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

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

uzilan
la source
6

Prenons un exemple: considérez que vous souhaitez créer deux applications:

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

médiateur

Construire l'application de chat, vous choisirez le mediatormodèle de conception.

  • Les personnes peuvent rejoindre et quitter le chat à un moment donné, il n'est donc pas logique de garder une référence directe entre deux personnes qui discutent.
  • Nous devons encore faciliter une communication entre deux personnes et leur permettre de discuter.

Pourquoi préférerons-nous le mediator? jetez un œil à sa définition:

Avec le modèle médiateur, la communication entre les objets est encapsulée dans un objet médiateur. Les objets ne communiquent plus directement les uns avec les autres, mais communiquent par le biais du médiateur. Cela réduit les dépendances entre les objets communicants, réduisant ainsi le couplage.

Comment fonctionne la magie? Tout d'abord, nous allons créer le médiateur de chat et faire en sorte que les objets de personnes s'y inscrivent, afin qu'il ait une connexion bidirectionnelle avec chaque personne (la personne peut envoyer un message en utilisant le médiateur de chat car elle y a accès, et le médiateur de chat y aura 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

En construisant l'application d'appel 911, vous choisirez le observermodèle de conception.

  • Chaque observerobjet ambulance souhaite être informé de l'état d'urgence, afin de pouvoir conduire l'adresse et apporter son aide.
  • L'opérateur d'urgence observablegarde une référence à chacun des ambulances observerset les informe lorsqu'une aide est nécessaire (ou génère un événement).

Pourquoi préférerons-nous le observer? jetez un œil à sa définition:

Un objet, appelé le sujet, maintient une liste de ses dépendants, appelés observateurs, et les notifie automatiquement de tout changement d'état, 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 mediatora une communication bidirectionnelle entre les objets personnes (envoi et réception) où l'opérateur observablen'a qu'une communication à sens unique (il indique à l'ambulance observerde conduire et de terminer).
  2. Le chat mediatorpeut faire interagir les objets personnes entre eux (même s'il ne s'agit pas d'une communication directe), les ambulances observersne s'enregistrent qu'aux observableévénements de l'opérateur .
  3. Chaque objet personne a une référence au chat mediator, et le chat mediatorgarde également une référence à chacune des personnes. Là où l'ambulance observerne garde pas la référence à l'opérateur observable, seul l'opérateur observablegarde la référence à chaque ambulance observer.
Shahar Shokrani
la source
3
Le dernier morceau aide. Le médiateur et l'observateur accomplissent tous deux le même objectif, mais le médiateur permet une communication bidirectionnelle tandis que l'observateur ne travaille que dans un seul sens.
kiwicomb123
Exactement, heureux que cela ait aidé
Shahar Shokrani
6

Bien que les deux soient utilisés pour une manière organisée de parler des 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. Ainsi, le changement se produit dans l'objet central qui est également chargé de le signaler. Cependant, dans Mediator, le changement d'état peut se produire dans n'importe quel objet, mais il est diffusé par un médiateur. Il y a donc une différence dans le flux. Mais je ne pense pas que cela affecte notre comportement de code. On peut utiliser l'un ou l'autre pour obtenir le même comportement. D'un autre côté, cette différence pourrait avoir des effets sur la compréhension conceptuelle du code.

Vous voyez, le but principal de l'utilisation de modèles est plutôt de créer un langage commun entre les développeurs. Ainsi, lorsque je vois un médiateur, je comprends personnellement plusieurs éléments qui tentent de communiquer sur un seul courtier / hub pour réduire le bruit de communication (ou pour promouvoir le SRP) et chaque objet est tout aussi important en termes de capacité à signaler un changement d'état. Par exemple, pensez à plusieurs avions s'approchant d'un aéroport. Chacun devrait communiquer sur le pylône (médiateur) plutôt que de communiquer les uns avec les autres. (Pensez à 1000 avions communiquant entre eux lors de l'atterrissage - ce serait un gâchis)

Cependant, quand je vois un observateur, cela signifie qu'il y a des changements d'état qui pourraient m'intéresser et que je devrais m'enregistrer / m'abonner pour écouter des changements d'état particuliers. Il y a un objet central chargé de signaler les changements d'état. Par exemple, si je me soucie d'un aéroport spécifique sur mon chemin de A à B, je peux m'inscrire à cet aéroport pour assister à des événements diffusés comme s'il y avait une piste vide ou quelque chose du genre.

J'espère que c'est clair.

stdout
la source
5

@cdc a parfaitement expliqué la différence d'intention.

Je vais ajouter quelques informations supplémentaires.

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

Médiateur : Centralisez la communication entre un ensemble d'objets, créés à partir d'une classe particulière.

Structure du modèle Mediator de dofactory :

entrez la description de l'image ici

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

Collègue : 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 objets Colleague et entretient ses collègues

ConcreteColleague : implémente les opérations de notification reçues via Mediator , qui ont été générées par d'autres collègues

Un exemple du monde réel:

Vous gérez un réseau d'ordinateurs en topologie maillée . Si un nouvel ordinateur est ajouté ou si un ordinateur existant est supprimé, tous les autres ordinateurs de ce réseau doivent connaître ces deux événements.

Voyons comment le modèle 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);
    }
}

production:

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 grâce à l'événement de registre. 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 rendue.
  3. Lorsque Penguin est ajouté au réseau, Eagle et Ostrich ont été informés: les lignes 2 et 3 de la sortie sont maintenant rendues.
  4. Lorsque Eagle a quitté le réseau à la suite d'un événement de désinscription, Ostrich et Penguin ont été informés. Les lignes 4 et 5 de la sortie sont maintenant rendues.
Ravindra babu
la source
2

Que diriez-vous de cette explication Techniquement, Observer et Mediator sont identiques et sont utilisés pour fournir un moyen découplé pour la communication des composants, mais l'utilisation est différente.

Tout en obeserver notifiant les composants abonnés des changements d'état (création d'un nouvel enregistrement de base de données, par exemple), les mediator commandes ont enregistré des composants pour faire quelque chose lié au flux de logique métier (envoi d'un courrier électronique à l'utilisateur pour réinitialiser le mot de passe).

Observateur

  • Les consommateurs de notification sont responsables de s'abonner pour recevoir des notifications
  • Le traitement des notifications ne fait pas partie du flux commercial

Médiateur

  • Inscription explicite requise pour connecter "éditeur" et "consommateurs"
  • Le traitement des notifications fait partie d'un flux commercial spécifique
Technologies Saturn
la source