Padrões de projeto orientados a objetos do mediador versus observador

91

Tenho lido o Gang Of Four , a fim de resolver alguns dos meus problemas e me deparei com o padrão Mediator .

Eu já havia usado o Observer em meus projetos para fazer alguns aplicativos GUI. Estou um pouco confuso porque não encontro grande diferença entre os dois. Procurei encontrar a diferença, mas não consegui encontrar uma resposta adequada para minha consulta.

Alguém poderia me ajudar a diferenciar os dois com algum bom exemplo que claramente demarque os dois?

Fooo
fonte
5
Meu pedido para migrar esta pergunta para Programmers.StackExchangefoi negado, mas fiz uma postagem semelhante porque estava interessado na resposta. Você pode achar algumas das respostas interessantes. :)
Rachel de
Para exemplos de JavaScript, você pode dar uma olhada na minha resposta a uma pergunta semelhante .
Alex Pakka
O livro GoF original trata disso no ponto 8 da seção de implementação, dando um exemplo de um ChangeManagerpara o Observerpadrão que usa Mediator. Vejo; paginas.fe.up.pt/~aaguiar/as/gof/hires/pat5g.htm#samplecode
robi-y

Respostas:

104

O padrão Observer: define uma dependência um-para-muitos entre objetos para que, quando um objeto muda de estado, todos os seus dependentes são notificados e atualizados automaticamente.

O padrão Mediator: define um objeto que encapsula como um conjunto de objetos interage. O mediador promove o acoplamento fraco, evitando que os objetos se refiram explicitamente uns aos outros e permite que você varie sua interação independentemente.

Fonte: dofactory

Exemplo:

O padrão do observador: Classe A, pode ter zero ou mais observadores do tipo O registrados nele. Quando algo em A é alterado, ele notifica todos os observadores.

O padrão do mediador: você tem algumas instâncias da classe X (ou talvez até vários tipos diferentes: X, Y e Z) e eles desejam se comunicar (mas você não quer que cada um tenha referências explícitas a cada um outro), então você cria uma classe de mediador M. Cada instância de X tem uma referência a uma instância compartilhada de M, por meio da qual pode se comunicar com as outras instâncias de X (ou X, Y e Z).

CDC
fonte
A explicação do observador parece estar mais próxima do padrão de comando do que do padrão do observador
Aun
39

No livro original que cunhou os termos Observador e Mediador, Padrões de Design, Elementos de Software Orientado a Objetos Reutilizáveis , ele diz que o padrão Mediador pode ser implementado usando o padrão observador. No entanto, também pode ser implementado fazendo com que os Colegas (que são aproximadamente equivalentes ao padrão Assuntos do Observador) tenham uma referência a uma classe Mediador ou a uma interface Mediador.

Existem muitos casos em que você gostaria de usar o padrão de observador, a chave é que um objeto não deve saber quais outros objetos estão observando seu estado.

O mediador é um pouco mais específico, evita que as classes se comuniquem diretamente, mas por meio de um mediador. Isso ajuda o princípio de Responsabilidade Única, permitindo que a comunicação seja transferida para uma classe que apenas lida com a comunicação.

Um exemplo clássico do Mediador é em uma GUI, onde a abordagem ingênua pode levar a um código em um evento de clique de botão dizendo "se o painel Foo estiver desabilitado e o painel Bar tiver um rótulo dizendo" Por favor, digite a data "e não chame o servidor, caso contrário, vá em frente ", onde com o padrão Mediator poderia dizer" Sou apenas um botão e não tenho nada a ver com o painel Foo e a etiqueta no painel Bar, então vou perguntar ao meu mediador se ligar para o servidor está OK agora. "

Ou, se o Mediator for implementado usando o padrão Observer, o botão dirá "Ei, observadores (o que incluiria o mediador), meu estado mudou (alguém clicou em mim). Faça algo a respeito, se quiser". No meu exemplo, isso provavelmente faz menos sentido do que referenciar diretamente o mediador, mas em muitos casos, usar o padrão Observer para implementar o Mediator faria sentido, e a diferença entre o Observer e o Mediator seria mais uma intenção do que uma diferença no próprio código.

psr
fonte
Eu estava procurando a palavra "princípio da responsabilidade única".
stdout
37

Observador

1. Sem

  • Cliente1 : Ei , sujeito , quando você muda?

  • Cliente2 : Quando você mudou de assunto ? Eu não percebi!

  • Cliente3 : Eu sei que o assunto mudou.

2. Com

  • Os clientes ficam em silêncio.
  • Algum tempo depois ...
  • Assunto : Caros clientes , mudei!

Mediador

1. Sem

  • Cliente1 : Ei , Taxi1 , leve-me a algum lugar.
  • Cliente2 : Ei , Taxi1 , me leve a algum lugar.
  • Cliente1 : Ei , Taxi2 , leve-me a algum lugar.
  • Cliente2 : Ei , Taxi2 , leve-me a algum lugar.

2. Com

  • Cliente1 : Olá , TaxiCenter , leve-me um táxi .
  • Cliente2 : Olá , TaxiCenter , leve-me um táxi .
Pmpr
fonte
2
Seu exemplo de mediador é o padrão de fábrica, não o padrão de mediador
Mohammad Karimi
2
@Pmpr É o padrão de design do mediador. O TaxiCenter não cria táxis, ele os disponibiliza de alguma forma (provavelmente cada táxi espera até que o TaxiCenter diga que é a sua vez)
Siva R
14

Esses padrões são usados ​​em diferentes situações:

O padrão de mediador é usado quando você tem dois subsistemas com alguma dependência e um deles deve ser alterado, e como você pode não querer mudar o sistema que depende do outro, você pode introduzir um mediador que irá desacoplar a dependência entre eles. Dessa forma, quando um dos subsistemas muda, tudo que você precisa fazer é atualizar o mediador.

O padrão observador é usado quando uma classe deseja permitir que outras classes se registrem e recebam notificações sobre eventos, por exemplo, ButtonListener etc.

Ambos os padrões permitem menor acoplamento, mas são bastante diferentes.

uzilan
fonte
6

Vamos por um exemplo: considere que você deseja construir dois aplicativos:

  1. Aplicativo de bate-papo.
  2. Aplicativo para operador de ambulância de emergência.

mediador

Ao construir o aplicativo de chat, você escolherá o mediatorpadrão de design.

  • As pessoas podem entrar e sair do chat a qualquer momento, portanto não faz sentido manter uma referência direta entre duas pessoas conversando.
  • Ainda precisamos facilitar a comunicação entre duas pessoas e permitir que elas conversem.

Por que vamos preferir o mediator? basta dar uma olhada em sua definição:

Com o padrão de mediador, a comunicação entre objetos é encapsulada em um objeto de mediador. Os objetos não se comunicam mais diretamente uns com os outros, mas se comunicam por meio do mediador. Isso reduz as dependências entre os objetos em comunicação, reduzindo assim o acoplamento.

Como funciona a magia? Primeiro iremos criar o mediador de chat e fazer com que os objetos de pessoas se registrem nele, então ele terá duas conexões direcionais com cada pessoa (a pessoa pode enviar mensagem usando o mediador de chat porque tem acesso a ele, e o mediador de chat irá acessar o método recebido do objeto pessoa porque ele também tem acesso a ele)

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

Ao construir o aplicativo de chamada para o 911, você escolherá o observerpadrão de design.

  • Cada ambulância observer objeto deseja ser informado quando houver um estado de emergência, para que ele possa dirigir até o endereço e dar ajuda.
  • O operador de emergência observablemantém referência a cada um da ambulância observerse os notifica quando ajuda é necessária (ou evento gerador).

Por que vamos preferir o observer? basta dar uma olhada em sua definição:

Um objeto, chamado de assunto, mantém uma lista de seus dependentes, chamados de observadores, e os notifica automaticamente sobre qualquer mudança de estado, geralmente chamando um de seus 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);

As diferenças:

  1. O chat mediatortem comunicação bidireccional entre os objectos da pessoa (enviar e receber), enquanto o operador observabletem comunicação apenas unilateral (avisa a ambulância observerpara conduzir e terminar).
  2. O chat mediatorpode fazer com que os objetos das pessoas interajam entre si (mesmo que não seja uma comunicação direta), as ambulâncias observerssó se cadastram nos observableeventos da operadora .
  3. Cada objeto de pessoa tem uma referência ao chat mediator, e também o chat mediatorguarda referência a cada uma das pessoas. Quando a ambulância observernão mantém referência ao operador observable, apenas o operador observablemantém referência a todas as ambulâncias observer.
Shahar Shokrani
fonte
3
A última parte ajuda. O mediador e o observador realizam o mesmo objetivo, no entanto, o mediador permite a comunicação bidirecional, enquanto o observador trabalha apenas em uma direção.
kiwicomb123,
Exatamente, que bom que ajudou
Shahar Shokrani,
6

Embora ambos sejam usados ​​para uma maneira organizada de falar sobre mudanças de estado, eles são ligeiramente diferentes estrutural e semanticamente da OMI.

Observer é usado para transmitir uma mudança de estado de um objeto específico, a partir do próprio objeto. Assim, a mudança ocorre no objeto central que também é responsável por sinalizá-lo. No entanto, no Mediator, a mudança de estado pode acontecer em qualquer objeto, mas é transmitida de um mediador. Portanto, há uma diferença no fluxo. Mas não acho que isso afete nosso comportamento de código. Podemos usar um ou outro para obter o mesmo comportamento. Por outro lado, essa diferença pode afetar a compreensão conceitual do código.

Veja, o objetivo principal de usar padrões é antes criar uma linguagem comum entre os desenvolvedores. Então, quando vejo um mediador, eu pessoalmente entendo vários elementos tentando se comunicar por meio de um único broker / hub para reduzir o ruído de comunicação (ou para promover SRP) e cada objeto é igualmente importante em termos de ter a capacidade de sinalizar uma mudança de estado. Por exemplo, pense em várias aeronaves se aproximando de um aeroporto. Cada um deve se comunicar pelo pilão (mediador) em vez de se comunicarem uns com os outros. (Pense em 1000 aeronaves comunicando-se entre si durante o pouso - seria uma bagunça)

No entanto, quando vejo um observador, significa que há algumas mudanças de estado com as quais posso me preocupar e devo registrar / assinar para ouvir mudanças de estado específicas. Existe um objeto central responsável por sinalizar mudanças de estado. Por exemplo, se eu me importo com um aeroporto específico no caminho de A para B, posso me registrar nesse aeroporto para assistir a alguns eventos transmitidos como se houvesse uma pista vazia ou algo parecido.

Espero que esteja claro.

saída padrão
fonte
5

@cdc explicou a diferença de intenção de maneira excelente.

Vou adicionar mais algumas informações sobre ele.

Observador : Habilita a notificação de um evento em um objeto para diferentes conjuntos de objetos (instâncias de diferentes classes)

Mediador : Centraliza a comunicação entre conjunto de objetos, criados a partir de uma determinada classe.

Estrutura do padrão de mediador da dofactory :

insira a descrição da imagem aqui

Mediador : Define uma interface para comunicação entre Colegas.

Colega : É uma classe abstrata, que define os eventos a serem comunicados entre Colegas

ConcreteMediator : Implementa comportamento cooperativo coordenando objetos Colleague e mantém seus colegas

ConcreteColleague : Implementa as operações de notificação recebidas por meio do Mediador , que foram geradas por outro Colega

Um exemplo do mundo real:

Você está mantendo uma rede de computadores no Mesh topologia . Se um novo computador for adicionado ou um computador existente for removido, todos os outros computadores dessa rede devem saber sobre esses dois eventos.

Vamos ver como o padrão do mediador se encaixa nele.

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

resultado:

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

Explicação:

  1. Eagle é adicionado à rede primeiro por meio do evento de registro. Nenhuma notificação a nenhum outro colega, já que o Eagle é o primeiro.
  2. Quando Ostrich é adicionado à rede, o Eagle é notificado: A linha 1 de saída é renderizada agora.
  3. Quando o Penguin é adicionado à rede, o Eagle e o Ostrich foram notificados: a linha 2 e a linha 3 de saída são renderizadas agora.
  4. Quando a Eagle saiu da rede por meio de um evento de cancelamento de registro, o Ostrich e o Penguin foram notificados. A linha 4 e a linha 5 de saída são renderizadas agora.
Ravindra babu
fonte
2

Como Sobre esta explicação Tecnicamente, tanto o Observer quanto o Mediator são iguais e são usados ​​para fornecer uma maneira desacoplada para a comunicação do componente, mas o uso é diferente.

Enquanto obeserver notifica os componentes assinados sobre mudanças de estado (criação de novo registro db, por exemplo), os mediator comandos registrados componentes para fazer algo relacionado ao fluxo de lógica de negócios (enviar email ao usuário para redefinição de senha).

Observador

  • Os consumidores de notificação são responsáveis ​​por se inscrever para receber notificações
  • O processamento de notificações não faz parte do fluxo de negócios

Mediador

  • Registro explícito necessário para conectar "editor" e "consumidores"
  • O processamento de notificações faz parte do fluxo de negócios específico
Saturn Technologies
fonte