Mediator vs. Observer
The Mediator and the Observer has some similarities, and can be very easily confused, in this post I would like to demonstrate their differences by two useful examples: 1. Chat application. 2. Emergency ambulance operator application.
Chat application (Mediator design pattern)
The mediator design pattern wikipedia definition is:
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
Building a chat application by the mediator design pattern is an easy choice, why? chat application is all about connecting between two users, the challenge with this type of systems if to connect two (or more) random users to each other, because users can come and go at any given moment.
So facilitating a direct connection between two users like when User A holds a reference for User B and vise versa:
Can lead to a problem when User B decides to leave the chat like:
This arrow getting out from User A, tells us that user A still hold a reference to User B even if he has left the building long ago, holding a reference is total waste.
As a solution we can always program the User A to check whether or not if that reference still needed or not but then we should implement this kind of logic for every user! so a better preferred solution is to facilitate a Mediator that holds the reference for all the users communication like:
That way when User B will leave the chat room, only the Mediator will be notify about it and remove the User B reference:
In this case User A still does not know that User B is gone, but he holds only one reference to the Mediator (and not to each user he is having conversation with).
Observer (Emergency ambulance operator application)
The observer design pattern Wikipedia definition is:
an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes
Building an emergency ambulance operator application we have to keep in mind that each ambulance (observer) is waiting on hold to be called to drive to a location according to the operator (observable), so in this case unlike the mediator design pattern, there is no need in two way communication between the ambulance and the operator, only the operator is notify the ambulances when they are needed and tells them to drive to a specific location:
In conclusion, the mediator allows us to conduct a two way communication between two objects, and the observer allows notify objects that something had happened in a one way communication manner.
A simple JS code examples for a mediator chat application:
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);
A simple JS code for observer emergency ambulance operator application:
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);
If you liked this post you can buy me a cup of coffee.
(based on my original stackoverflow answer here)