The Mediator Pattern
For an optional assignment in the Advanced Java class at school, we got to choose one GoF pattern and present it to the class. I chose the Mediator pattern for its complexity and usefulness in user interface programming.
Mediator Pattern Description
Mediator is an object-based pattern of the Behavioral Patterns group, and its purpose is described in the GoF book as follows:
“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.” [GoF]
The Mediator pattern is used to centralize communications between objects. Instead of the objects sending messages to each other, they only refer to the mediator which in turn informs the other objects as needed.
Use Cases for the Pattern
While high cohesion is a desirable attribute of software systems, it may lead to many connections between objects, and thus to monolithic systems where objects may not work properly without the support of other objects.
A common use case for the Mediator pattern are view components in user interfaces consisting of many objects (buttons, text fields, checkboxes etc.) which are all connected to each other. Therefore, the Mediator pattern is commonly used in user interface programming.
Let's have a look at a UI dialog to generate passwords:
Figure 1 - GUI mockup with object relations annotations
Figure 1 shows some possible relations between the UI elements of our password creation dialog. The number fields for password count and password length would be activated only if at least one of the character group checkboxes were ticked. Furthermore, the OK button would remain deactivated until all form elements contained valid values. Finally, the password count depends on the combination of provided password length and selected characters. Long story short, even with this rather simple dialog, there are many dependencies. And these are just a few ones off the top of my head!
Let's have a look at the object coupling of our example dialog:
Figure 2 – Object relations without a mediator object
As we can see, the GUI elements are rather tightly coupled, meaning there are many dependencies between the objects. By introducing a mediator object, we achieve a looser coupling with each object only knowing itself and the mediator object:
Figure 3 – Object relations after introducing a mediator object
We still have a lot of connections, but in contrast to the previous design, the communication is now centralized, thanks to our brand spanking new mediator object of type PasswordDialogDirector. And that's the raison d'être of the Mediator pattern – not to make your user interface logic less complex, but to centralize the communication, and by doing so loosen the coupling between the objects. If a new element is added in the future, only the mediator object will have to be amended to incorporate the added logic.
There are of course other use cases for the Mediator pattern than graphical user interfaces. The use of a mediator object should be considered whenever there are many objects communicating with each other, and this communication can be simplified by centralizing it.
The Façade is mentioned in [GoF] as a pattern similar to the Mediator. However, the purpose of the Façade is not to simplify the communication between objects, but to provide a simplified view on a complex subsystem. The Façade pattern works unidirectional, meaning there are only messages being sent from the façade object to the subsystem objects, but never the other way around. The Mediator, on the other hand, works in a multidirectional fashion by having messages sent from the objects to the mediator and vice versa.
The Observer pattern also allows registering objects with another object and having them notified automatically if necessary. However, it does not centralize the communication between several objects in the same fashion as the Mediator pattern does. In fact, the Observer pattern is often used in Mediator implementations to register objects with the mediator instance.
I've put an example implementation of the Mediator pattern on GitHub as Gist. Check it out!