Patrones de diseño orientado a objetos de Mediator Vs Observer


93

He estado leyendo Gang Of Four para resolver algunos de mis problemas y encontré el patrón Mediator .

Anteriormente había usado Observer en mis proyectos para hacer alguna aplicación GUI. Estoy un poco confundido porque no encuentro gran diferencia entre los dos. Busqué para encontrar la diferencia pero no pude encontrar ninguna respuesta adecuada para mi consulta.

¿Podría alguien ayudarme a diferenciar entre los dos con algún buen ejemplo que los demarque claramente?


5
Mi solicitud para migrar esta pregunta Programmers.StackExchangefue denegada, pero hice una publicación similar allí porque estaba interesado en la respuesta. Puede que algunas de las respuestas te resulten interesantes. :)
Rachel

Para ver ejemplos de JavaScript, puede echar un vistazo a mi respuesta a una pregunta similar .
Alex Pakka

Las direcciones originales del libro GoF que bajo el punto de aplicación la sección # 8, dando un ejemplo de una ChangeManagerpara el Observerpatrón que usos Mediator. ver; paginas.fe.up.pt/~aaguiar/as/gof/hires/pat5g.htm#samplecode
robi-y

Respuestas:


104

El patrón de observador: define una dependencia de uno a muchos entre objetos para que cuando un objeto cambie de estado, todos sus dependientes sean notificados y actualizados automáticamente.

El patrón del mediador: define un objeto que encapsula cómo interactúa un conjunto de objetos. Mediator promueve el acoplamiento flexible al evitar que los objetos se refieran entre sí de forma explícita y le permite variar su interacción de forma independiente.

Fuente: dofactory

Ejemplo:

El patrón de observadores: Clase A, puede tener cero o más observadores de tipo O registrados. Cuando se cambia algo en A, notifica a todos los observadores.

El patrón del mediador: tienes algunas instancias de clase X (o tal vez incluso varios tipos diferentes: X, Y y Z), y desean comunicarse entre sí (pero no quieres que cada una tenga referencias explícitas a cada una). otro), por lo que crea una clase mediadora M. Cada instancia de X tiene una referencia a una instancia compartida de M, a través de la cual puede comunicarse con las otras instancias de X (o X, Y y Z).


La explicación del observador parece estar más cerca del patrón de comando que del patrón del observador
Aun

40

En el libro original que acuñó los términos Observador y Mediador, Patrones de diseño, Elementos de software orientado a objetos reutilizable , se dice que el patrón Mediador se puede implementar utilizando el patrón de observador. Sin embargo, también se puede implementar haciendo que los colegas (que son aproximadamente equivalentes al patrón de sujetos del observador) tengan una referencia a una clase de mediador o una interfaz de mediador.

Hay muchos casos en los que desea utilizar el patrón de observador, la clave es que un objeto no debe saber qué otros objetos están observando su estado.

El mediador es un poco más específico, evita que las clases se comuniquen directamente, sino a través de un mediador. Esto ayuda al principio de Responsabilidad Única al permitir que la comunicación se descargue a una clase que solo maneja la comunicación.

Un ejemplo clásico de Mediator está en una GUI, donde el enfoque ingenuo puede llevar a un código en un evento de clic de botón que dice "si el panel Foo está deshabilitado y el panel de la barra tiene una etiqueta que dice" Ingrese la fecha ", entonces no llame al servidor, de lo contrario, adelante ", donde con el patrón de Mediador podría decir" Soy solo un botón y no tengo nada que ver con conocer el panel de Foo y la etiqueta en el panel de la barra, así que solo le preguntaré a mi mediador si llama al servidor está bien ahora ".

O, si Mediator se implementa usando el patrón Observer, el botón diría "Hey, observadores (que incluiría al mediador), mi estado cambió (alguien hizo clic en mí). Haga algo al respecto si le importa". En mi ejemplo, eso probablemente tiene menos sentido que hacer referencia directa al mediador, pero en muchos casos usar el patrón Observer para implementar Mediator tendría sentido, y la diferencia entre Observer y Mediator sería más una de intención que una diferencia en el código mismo.


Estaba buscando esta palabra "principio de responsabilidad única".
salida

37

Observador

1. Sin

  • Cliente1 : Oye Asunto , ¿cuándo cambias?

  • Cliente2 : ¿Cuándo cambió de Asunto ? ¡No me he dado cuenta!

  • Cliente3 : Sé que el Asunto ha cambiado.

2. Con

  • Los clientes guardan silencio.
  • Algún tiempo después ...
  • Asunto : Estimados clientes , ¡he cambiado!

Mediador

1. Sin

  • Cliente1 : Hola Taxi1 , llévame a algún lugar.
  • Cliente2 : Hola Taxi1 , llévame a algún lugar.
  • Cliente1 : Hola Taxi2 , llévame a algún lugar.
  • Cliente2 : Hola Taxi2 , llévame a algún lugar.

2. Con

  • Cliente1 : Hola TaxiCenter , por favor, llévame un taxi .
  • Cliente2 : Hola TaxiCenter , por favor, llévame un taxi .

2
Su ejemplo de mediador es un patrón de fábrica, no un patrón de mediador
Mohammad Karimi

2
@Pmpr Es el patrón de diseño del mediador. TaxiCenter no creará taxis, hace que los taxis estén disponibles de alguna manera (probablemente cada taxi espera hasta que TaxiCenter diga que es su turno)
Siva R

14

Estos patrones se utilizan en diferentes situaciones:

El patrón de mediador se utiliza cuando tiene dos subsistemas con alguna dependencia y uno de ellos debe cambiar, y dado que es posible que no desee cambiar el sistema que depende del otro, es posible que desee introducir un mediador que desacoplar la dependencia entre ellos. De esa manera, cuando uno de los subsistemas cambia, todo lo que tiene que hacer es actualizar el mediador.

El patrón de observador se usa cuando una clase quiere permitir que otras clases se registren y reciban notificaciones sobre eventos, por ejemplo, ButtonListener, etc.

Ambos patrones permiten un acoplamiento menor, pero son bastante diferentes.


7

Veamos un ejemplo: considere que desea construir dos aplicaciones:

  1. Aplicación de chat.
  2. Aplicación de operador de ambulancia de emergencia.

mediador

Al construir la aplicación de chat, elegirá el mediatorpatrón de diseño.

  • Las personas pueden unirse y salir del chat en cualquier momento, por lo que no tiene ningún sentido mantener una referencia directa entre dos personas que conversan.
  • Todavía tenemos que facilitar la comunicación entre dos personas y permitirles charlar.

¿Por qué preferiremos el mediator? solo eche un vistazo a su definición:

Con el patrón mediador, la comunicación entre objetos se encapsula dentro de un objeto mediador. Los objetos ya no se comunican directamente entre sí, sino que se comunican a través del mediador. Esto reduce las dependencias entre los objetos en comunicación, reduciendo así el acoplamiento.

¿Cómo funciona la magia? Primero crearemos el mediador de chat y haremos que los objetos de las personas se registren en él, por lo que tendrá una conexión bidireccional con cada persona (la persona puede enviar un mensaje usando el mediador de chat porque tiene acceso a él y el mediador de chat accederá el método recibido de la persona objeto porque él también tiene acceso a él)

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

observador

Al crear la aplicación de llamada al 911, elegirá el observerpatrón de diseño.

  • Cada observerobjeto de la ambulancia desea ser informado cuando hay un estado de emergencia, para poder conducir la dirección y brindar ayuda.
  • El operador de emergencia observablemantiene la referencia a cada una de las ambulancias observersy les notifica cuando se necesita ayuda (o evento generador).

¿Por qué preferiremos el observer? solo eche un vistazo a su definición:

Un objeto, llamado sujeto, mantiene una lista de sus dependientes, llamados observadores, y les notifica automáticamente de cualquier cambio de estado, generalmente llamando a uno de sus métodos.

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

Las diferencias:

  1. El chat mediatortiene comunicación bidireccional entre los objetos de las personas (enviar y recibir) donde el operador observablesolo tiene comunicación unidireccional (le dice a la ambulancia observerque conduzca y termine).
  2. El chat mediatorpuede hacer que los objetos de las personas interactúen entre ellos (incluso si no es una comunicación directa), las ambulancias observerssolo se registran en los observableeventos del operador .
  3. Cada objeto de persona tiene una referencia al chat mediator, y también el chat mediatormantiene una referencia a cada una de las personas. Donde la ambulancia observerno mantiene la referencia al operador observable, solo el operador observablemantiene la referencia a cada ambulancia observer.

3
Lo último ayuda. El mediador y el observador logran el mismo objetivo, sin embargo, el mediador permite la comunicación bidireccional mientras que el observador trabaja de una sola manera.
kiwicomb123

Exactamente, me alegro de haber ayudado
Shahar Shokrani

6

Aunque ambos se utilizan para una forma organizada de informar sobre los cambios de estado, en mi opinión son ligeramente diferentes estructural y semánticamente.

El observador se utiliza para transmitir un cambio de estado de un objeto en particular, desde el propio objeto. Entonces el cambio ocurre en el objeto central que también se encarga de señalizarlo. Sin embargo, en Mediator, el cambio de estado puede ocurrir en cualquier objeto, pero se transmite desde un mediador. Entonces hay una diferencia en el flujo. Pero no creo que esto afecte el comportamiento de nuestro código. Podemos utilizar uno u otro para lograr el mismo comportamiento. Por otro lado, esta diferencia podría tener algunos efectos en la comprensión conceptual del código.

Vea, el propósito principal de usar patrones es más bien crear un lenguaje común entre desarrolladores. Entonces, cuando veo a un mediador, personalmente entiendo múltiples elementos que intentan comunicarse a través de un solo corredor / concentrador para reducir el ruido de la comunicación (o promover SRP) y cada objeto es igualmente importante en términos de tener la capacidad de señalar un cambio de estado. Por ejemplo, piense en varias aeronaves que se acercan a un aeropuerto. Cada uno debe comunicarse a través del pilón (mediador) en lugar de comunicarse entre sí. (Piense en 1000 aviones que se comunican entre sí al aterrizar, eso sería un desastre)

Sin embargo, cuando veo a un observador, significa que hay algunos cambios de estado que podrían interesarme y debería registrarme / suscribirme para escuchar cambios de estado particulares. Hay un objeto central responsable de señalar los cambios de estado. Por ejemplo, si me preocupa un aeropuerto específico en mi camino de A a B, puedo registrarme en ese aeropuerto para ver algunos eventos transmitidos, como si hubiera una pista vacía o algo así.

Espero que esté claro.


5

@cdc explicó la diferencia de intenciones de manera excelente.

Agregaré más información encima.

Observador : permite la notificación de un evento en un objeto a diferentes conjuntos de objetos (instancias de diferentes clases)

Mediador : Centraliza la comunicación entre un conjunto de objetos, creado a partir de una clase en particular.

Estructura del patrón de Mediador de dofactory :

ingrese la descripción de la imagen aquí

Mediador : define una interfaz para la comunicación entre colegas.

Colega : Es una clase abstracta, que define los eventos que se comunicarán entre Colegas.

ConcreteMediator : implementa el comportamiento cooperativo coordinando objetos de colega y mantiene a sus colegas

ConcreteColleague : Implementa las operaciones de notificación recibidas a través de Mediator , que ha sido generada por otro Colega

Un ejemplo del mundo real:

Mantienes una red de computadoras en Mesh topología de . Si se agrega una computadora nueva o se quita una computadora existente, todas las demás computadoras en esa red deben conocer estos dos eventos.

Veamos cómo encaja el patrón Mediator.

Fragmento de código:

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

salida:

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

Explicación:

  1. Eagle se agrega a la red al principio mediante un evento de registro. No hay notificaciones a otros colegas ya que Eagle es el primero.
  2. Cuando se agrega Ostrich a la red, se notifica a Eagle : la línea 1 de salida se representa ahora.
  3. Cuando se agrega Penguin a la red, se notifica a Eagle y Ostrich : la línea 2 y la línea 3 de salida ahora se renderizan.
  4. Cuando Eagle dejó la red a través de un evento de cancelación de registro, se notificó a Ostrich y Penguin . La línea 4 y la línea 5 de salida se renderizan ahora.

2

¿Qué tal esta explicación? Técnicamente, tanto Observer como Mediator son iguales y se utilizan para proporcionar una vía desacoplada para la comunicación de componentes, pero el uso es diferente.

Si bien obeserver notifica a los componentes suscritos sobre los cambios de estado (creación de un nuevo registro de base de datos, por ejemplo), los mediator comandos registrados componentes para hacer algo relacionado con el flujo de la lógica empresarial (enviar un correo electrónico al usuario para restablecer la contraseña).

Observador

  • Los consumidores de notificaciones son responsables de suscribirse para recibir notificaciones
  • El procesamiento de notificaciones no forma parte del flujo comercial

Mediador

  • Se requiere un registro explícito para conectar "editor" y "consumidores"
  • El procesamiento de notificaciones es parte de un flujo comercial específico
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.