Mediator Vs Observer Object-Oriented Design Patterns

筅森魡賤 提交于 2019-11-27 02:57:10

The Observer pattern: Defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

The Mediator pattern: Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.

Source: dofactory

Example:

The observer pattern: Class A, can have zero or more observers of type O registered with it. When something in A is changed it notifies all of the observers.

The mediator pattern: You have some number of instances of class X (or maybe even several different types:X, Y & Z), and they wish to communicate with each other (but you don't want each to have explicit references to each other), so you create a mediator class M. Each instance of X has a reference to a shared instance of M, through which it can communicate with the other instances of X (or X, Y and Z).

In the original book that coined the terms Observer and Mediator, Design Patterns, Elements of Reusable Object-Oriented Software, it says that the Mediator pattern can be implemented by using the observer pattern. However it can also be implemented by having Colleagues (which are roughly equivalent to the Subjects of the Observer pattern) have a reference to either a Mediator class or a Mediator interface.

There are many cases when you would want to use the observer pattern, they key is that an object should not know what other objects are observing it's state.

Mediator is a little more specific, it avoids having classes communicate directly but instead through a mediator. This helps the Single Responsibility principle by allowing communication to be offloaded to a class that just handles communication.

A classic Mediator example is in a GUI, where the naive approach might lead to code on a button click event saying "if the Foo panel is disabled and Bar panel has a label saying "Please enter date" then don't call the server, otherwise go ahead", where with the Mediator pattern it could say "I'm just a button and have no earthly business knowing about the Foo panel and the label on the Bar panel, so I'll just ask my mediator if calling the server is O.K. right now."

Or, if Mediator is implemented using the Observer pattern the button would say "Hey, observers (which would include the mediator), my state changed (someone clicked me). Do something about it if you care". In my example that probably makes less sense then directly referencing the mediator, but in many cases using the Observer pattern to implement Mediator would make sense, and the difference between Observer and Mediator would be more one of intent than a difference in the code itself.

Observer

1. Without

  • Client1: Hey Subject, when do you change?

  • Client2: When did you change Subject? I have not noticed!

  • Client3: I know that Subject has changed.

2. With

  • Clients are silent.
  • Some time later ...
  • Subject: Dear clients, I have changed!

Mediator

1. Without

  • Client1: Hey Taxi1, take me some where.
  • Client2: Hey Taxi1, take me some where.
  • Client1: Hey Taxi2, take me some where.
  • Client2: Hey Taxi2, take me some where.

2. With

  • Client1: Hey TaxiCenter, please take me a Taxi.
  • Client2: Hey TaxiCenter, please take me a Taxi.

These patterns are used in different situations:

The mediator pattern is used when you have two sub-systems with some dependency and one of them is due for a change, and since you might not want to change the system that depends on the other, you may want to introduce a mediator which will decouple the dependency between them. That way, when one of the sub-systems changes, all you have to do is to update the mediator.

The observer pattern is used when a class wants to allow other classes to register themselves and receive notifications upon events, e. g. ButtonListener etc.

Both of these patterns allow for lesser coupling, but are quite different.

Although both of them are used for organised way of telling about state changes, they're slightly different structurally and semantically IMO.

Observer is used to broadcast a state change of a particular object, from the object itself. So the change happens in the central object that is also responsible for signalling it. However, in Mediator, state change can happen in any object but it's broadcasted from a mediator. So there's a difference in the flow. But, I don't think this affects our code behaviour. We can use one or another to achieve the same behaviour. On the other hand, this difference might have some affects on conceptual understanding of the code.

See, the primary purpose of using patterns is rather to create a common language between developers. So, when I see a mediator, I personally understand multiple elements trying to communicate over a single broker/hub to reduce communication noise (or to promote SRP) and each object is equally important in terms of having the ability of signalling a state change. For example, think of multiple aircrafts approaching to an airport. Each should communicate over the pylon (mediator) rather than communicating with each other. (Think of 1000 aircrafts communicating with each other when landing - that would be a mess)

However, when I see an observer, it means there're some state changes I might be care about and should register/subscribe to listen particular state changes. There's a central object responsible for signalling state changes. For example, if I care about a specific airport on my way from A to B, I can register to that airport to catch some events broadcasted like if there's an empty runway or something like that.

Hope it's clear.

@cdc explained the difference in intent excellently.

I will add some more info on top it.

Observer : Enables notification of a event in one object to different set of objects ( instances of different classes)

Mediator: Centralize the communication between set of objects, created from a particular class.

Structure of Mediator pattern from dofactory:

Mediator: Defines an interface for communication between Colleagues.

Colleague: Is an abstract class, which defines the events to be communicated between Colleagues

ConcreteMediator: Implements cooperative behavior by coordinating Colleague objects and maintains its colleagues

ConcreteColleague: Implements the notification operations received through Mediator, which has been generated by other Colleague

One real world example:

You are maintaining a network of computers in Mesh topology. If a new computer is added Or existing computer is removed, all other computers in that network should know about these two events.

Let's see how Mediator pattern fits into it.

Code snippet:

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

output:

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

Explanation:

  1. Eagle is added to network at first through register event. No notifications to any other colleagues since Eagle is the first one.
  2. When Ostrich is added to the network, Eagle is notified : Line 1 of output is rendered now.
  3. When Penguin is added to network, both Eagle and Ostrich have been notified : Line 2 and Line 3 of output is rendered now.
  4. When Eagle left the network through unregister event, both Ostrich and Penguin have been notified. Line 4 and Line 5 of output is rendered now.

Lets go by an example: consider you want to build two application:

  1. Chat application.
  2. Emergency ambulance operator application.

mediator

Building the chat application you will be choosing the mediator design pattern.

  • The persons may be joining and leaving the chat at any given time, so it does not make any sense to keep direct reference between two persons chatting.
  • We still need to facilitate a communication between two persons and allow them have a chat.

Why will we prefer the mediator? just have a look at its definition:

With the mediator pattern, communication between objects is encapsulated within a mediator object. Objects no longer communicate directly with each other, but instead communicate through the mediator. This reduces the dependencies between communicating objects, thereby reducing coupling.

How is the magic works? First we will create the chat mediator and make the persons objects register to it, so it will have two directional connection with every single person (the person can send message using the chat mediator cause it ha access to it, and the chat mediator will access the received method of the person object cause he also has access to it)

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

observer

Building the 911 call application you will be choosing the observer design pattern.

  • Each ambulance observer object wishes to be informed when there is an emergency state, so he can drive the address and give help.
  • The emergency operator observable keep reference to each on of the ambulance observers and notify them when help is needed (or generating event).

Why will we prefer the observer? just have a look at its definition:

An object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.

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

The Differences:

  1. The chat mediator has two way communication between the persons objects (send and receive) wheres the operator observable has only one way communication (It tell the ambulance observer to drive and finish).
  2. The chat mediator can make the persons objects interact between them (even if it not a direct communication), the ambulances observers only registers to the operator observable events.
  3. Each person object has a reference to the chat mediator, and also the chat mediator keep reference to the every one of the persons. Wheres the ambulance observer does not keep reference to the operator observable, only the operator observable keep reference to every ambulance observer.

How About this explanation Technically both Observer and Mediator are the same and are used to provide decoupled way for component communication, but usage is different.

While obeserver notifies subscribed components about state changes (creation of new db record, for instance), the mediator commands registered components to do something related to business logic flow (sending email to user for password reset).

Observer

  • Notification consumers are responsible to subscribe in order to receive notifications
  • Notification processing is not part of business flow

Mediator

  • Explicit registration required to connect "publisher" and "consumers"
  • Notification processing is part of specific business flow
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!