Design Patterns: Mediator
The mediator pattern belongs in the family of Behavioral Patterns. It contains the entire logic of how objects interact with each other. So, it helps us decouple objects.
What’s the problem
In case we have many objects that interact with each other, we end up with very complex and nonmaintainable code. That’s why it is a good case to use the Mediator design pattern.
There are many times that we have interaction among objects. We usually create a sufficient amount of objects allowing us to communicate with each other with the intention of building entities that act together for a specific reason.
When something happens to an object then another one has to do something or change its behavior.
Building a complex code allowing many objects (2 or more) to communicate with each other, you will end up with spaghetti code, nonmaintainable, and you will face a nightmare every time you want to find and fix a bug or to enhance the code.
This problem increases if you normally have a back and forth and complex interaction.
How do I solve this problem
The solution comes using the Mediator pattern. This pattern allows you to keep the whole logic of communication in one place (object) and every object used to interact with other objects now interacts only with the one which is aware of how to act on every occasion.
So, we can understand very quickly that objects are decoupled. The code is more simple than it used to be. That’s exactly what we try to achieve using OOP and design patterns.
Drawbacks
Using the Mediator pattern and keeping the whole interaction among objects in one place (object) means that we are going to move complexity from one place to the other.
It is very easy to turn the Mediator pattern into a mess if you don’t separate objects appropriately. Even if you want to use the same objects but somehow different, you have to keep each behavior in separate mediator objects.
So, there is a need to simplify the mediator if necessary.
How do I code the Mediator Pattern?
It is really easy to write code with a mediator pattern.
We start with classes that encapsulate mediators and instead of interacting with each other, they interact with the mediator.
Every time each class needs to interact with an object, it calls a method from the mediator and only the mediator knows what object needs to be called.
For the mediator, we just need an interface. It is a good practice to use an interface or an abstract class for the mediator. In this way, you are able to have different implementation of the mediator object.
Example
Greek public services are a mess. Each time a Greek citizen needs to use a public service, he faces a huge bureaucracy. So, a citizen has to transfer and get a bunch of documents from one public service to the other. In this case the Greek citizen is the mediator. He is the one who has to go and find the right document in a group of public services. This is wrong from many aspects but it is a reality.
Let’s start our example by implementing this situation but a little differently, in the right way. Instead of using citizen as an intermediary mean, we will create a mediator which is going to be aware of all public services and how to use them. So, the citizen can go and ask the mediator for something and it is going to interact with public services (objects) for the desirable result.
In the following example, a Greek citizen who wants to start a new company is represented.
We start with an interface for the mediator.
1 2 3 4 5 6 7 |
public interface Mediator { public void startNewFirm(); public void getIrsDoc(ArrayList<String> documents); public void getChamberDoc(ArrayList<String> documents); public void getInsuranceCarrierDoc(ArrayList<String> documents); public void firmIsReady(ArrayList<String> documents); } |
Let’s implement this mediator.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
public class ServiceMediator implements Mediator { private ArrayList<String> documents; private Office irs; private Office chamber; private Office insuranceCarrier; public ServiceMediator(){ documents = new ArrayList<String>(); irs = new Irs(this); chamber = new Chamber(this); insuranceCarrier = new InsuranceCarrier(this); } @Override public void startNewFirm(){ getIrsDoc(documents); } @Override public void getIrsDoc(ArrayList<String> documents) { this.documents = documents; irs.addNewDocument(this.documents); } @Override public void getChamberDoc(ArrayList<String> documents) { this.documents = documents; chamber.addNewDocument(this.documents); } @Override public void getInsuranceCarrierDoc(ArrayList<String> documents) { this.documents = documents; insuranceCarrier.addNewDocument(this.documents); } @Override public void firmIsReady(ArrayList<String> documents) { this.documents = documents; System.out.println("Congratulations! Your documents are ready and are the following: "); for (String document : documents) { System.out.println("\t" + document); } System.out.println("Your new firm is ready."); } } |
Now it’s time to implement the objects which are going to interact with the mediator.
1 2 3 4 5 6 7 8 9 10 11 |
abstract class Office { protected Mediator mediator; public Office(Mediator mediator){ this.mediator = mediator; } public abstract void addNewDocument(ArrayList<String> documents); protected abstract void nextStep(ArrayList<String> documents); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class Irs extends Office { public Irs(Mediator mediator) {super(mediator);} @Override public void addNewDocument(ArrayList<String> documents) { documents.add("Document from Irs."); nextStep(documents); } @Override protected void nextStep(ArrayList<String> documents){ this.mediator.getChamberDoc(documents); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class Chamber extends Office { public Chamber(Mediator mediator) {super(mediator);} @Override public void addNewDocument(ArrayList<String> documents) { documents.add("Document from Chamber."); nextStep(documents); } @Override protected void nextStep(ArrayList<String> documents){ this.mediator.getInsuranceCarrierDoc(documents); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class InsuranceCarrier extends Office { public InsuranceCarrier(Mediator mediator) {super(mediator);} @Override public void addNewDocument(ArrayList<String> documents) { documents.add("Document from Insurance Carrier."); nextStep(documents); } @Override protected void nextStep(ArrayList<String> documents){ this.mediator.firmIsReady(documents); } } |
The following code is representing the Greek citizen.
1 2 3 4 5 6 7 8 9 10 |
public abstract class Citizen { protected Mediator mediator; public Citizen(Mediator mediator){ this.mediator = mediator; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class Citizen1 extends Citizen{ private ArrayList<String> documents; public Citizen1(Mediator mediator){ super(mediator); } public void newFirm(){ mediator.startNewFirm(); } } |
Let’s create a function which runs the code
1 2 3 4 5 6 |
private void mediatorTesting(){ Mediator mediator = new ServiceMediator(); Citizen1 citizen = new Citizen1(mediator); citizen.newFirm(); } |
Output:
1 2 3 4 5 |
Congratulations! Your documents are ready and are the following: Document from Irs. Document from Chamber. Document from Insurance Carrier. Your new firm is ready. |
You can download the source code from GitHub.
Haha, I love the political insinuation! Greek public services rock!!