This chapter covers the mediator pattern.
GoF Definition
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.
Concept
A mediator takes the responsibility of controlling and coordinating the interactions of a specific group of objects that cannot refer to each other explicitly. So, you can imagine a mediator as an intermediary through whom these objects talk to each other. This kind of implementation helps reduce the number of interconnections among different objects. As a result, you can promote loose coupling in the system.
So, in this design, object communications are encapsulated with a mediator object so that they cannot communicate directly with each other and you reduce the dependencies among them.
Real-World Example
When a flight needs to take off, a series of verifications takes place. These kinds of verifications conform that all components/parts (which are dependent on each other) are in perfect condition.
Also consider when airplane pilots (approaching or departing the terminal area) communicate with the towers. They do not explicitly communicate with other pilots from different airlines. They only send their status to the tower. These towers also send the signals to conform which airplane can take-off or land. You must note that these towers do not control the whole flight. They only enforce constraints in the terminal areas.
Computer-World Example
When a client processes a business application, the developer may need to put some constraints on it. For example, a form in which a client needs to supply a user ID and password to access their account. On the same form, the client must supply other information, such as email, address, age, and so forth. Let’s assume that the developer applied the constraints as follows.
Initially, the application checks whether the ID supplied by the user is valid or not. If it is a valid user ID, then only the password field is enabled. After supplying these two fields, the application form needs to check whether the email address was provided by the user. Let’s further assume that after providing all of this information (a valid user ID, password, a correctly formatted email, etc.), the Submit button is enabled. So, basically the Submit button is enabled if the client supplies a valid user ID, password, email, and other mandatory details in the correct order. The developer may also enforce that the user ID must be an integer, so if the user mistakenly places any characters in that field, the Submit button stays in disabled mode. The mediator pattern becomes handy in such a scenario.
So, when a program consists of many classes and the logic is distributed among them, the code becomes harder to read and maintain. In those scenarios, if you want to bring new changes in the system’s behavior, it can be difficult unless you use the mediator pattern.
Note
The execute() method inside the java.util.concurrent.Executor interface follows this pattern.
The javax.swing.ButtonGroup class is another example that supports this pattern. This class has a method setSelected() that ensures that the user provides a new selection.
The different overloaded versions of various schedule() methods of the java.util.Timer class also can be considered to follow this pattern.
Illustration
Mediator: Defines the interface to provide the communication among Colleague objects.
ConcreteMediator: Maintains the list of the Colleague objects. It implements the Mediator interface and coordinates the communication among the Colleague objects.
Colleague: Defines the interface for communication with other Colleagues.
ConcreteColleague1 and ConcreteColleague2: Implements the Colleague interface. These objects communicate with each other through the mediator.
In this chapter, I provide two implementations of this pattern. In the first implementation, I replaced the word Colleague with Employee. Also, ConcreteColleague1 and ConcreteColleague2 are replaced with JuniorEmployee and SeniorEmployee, respectively. Let’s assume that you have three employees: Amit, Sohel, and Raghu, where Amit and Sohel are junior employees who report to their boss, Raghu, who is a senior employee. Raghu wants to smoothly coordinate things. Let’s further assume that they can communicate with each other through a chat server.
In the following implementation, Mediator is an interface that has two methods: register() and sendMessage(). The register() method registers an employee with the mediator and sendMessage() posts messages to the server. The ConcreteMediator class is the concrete implementation of the Mediator interface.
You can see that when an employee invokes the sendMessage() method, it is invoking mediator’s sendMessage() method. So, the actual communication process is conducted through the mediator.
In the client code, I introduced another person, Jack. But he did not register himself with the mediator object. So, the mediator is not allowing him to post any messages to this server.
Now go through the code and the corresponding output.
Class Diagram
Package Explorer View
Implementation
Output
Analysis
Note that only registered users can communicate with each other and successfully post messages on the chat server. The mediator does not allow any outsiders into the system. (Notice the last line of the output.)
Modified Illustration
The messages are only passing in one direction.
When one participant posts a message, everyone can see the message. So, there is no privacy.
If an employee forgets to register himself, he is not allowed to send a message. It is fine, but he should not be treated like an outsider. In a normal scenario, an organization outsider should be treated differently from an employee of the organization who forgets to register himself on the server.
The client code needed to register the participants to the mediator. Though you may argue that it is not a drawback, you may opt for a better approach. For example, you may register the participants automatically to a mediator when you create an Employee object inside the client code.
You have not used the employeeType() method in client code.
The JuniorEmployee and SeniorEmployee classes are replaced with a single ConcreteEmployee class. It helps us easily identify who belongs to the organization and who does not (in other words, outsiders).
In the modified implementation, each of these participants can see who is posting messages, but it is not disclosed to whom it is targeted or what the actual message is. So, there is privacy between two participants, but this approach can help someone like Raghu to coordinate things because he may interfere if he sees that employees are chatting too much.
In the client code, you create participants like the following.
The employeeType() method determines whether a participant is from inside the organization or if he or she is an outsider. In this context, you may also note that instead of using the following line
I used the former one for better readability.
Modified Class Diagram
Modified Package Explorer View
Modified Implementation
Modified Output
Analysis
Notice that when the employee named Jack (who belongs to the organization) sends a message without registering himself, the system prevents him from posting the message but gives him a suggestion. But Divya, who is an organization outsider, is told that she is not allowed to enter into the system. It also warns others.
Q&A Session
- 1.
Why are you complicating the things? In the first example, each of the participants could talk to each other directly and you could bypass the use of mediator. Is this correct?
In this example, you have only three registered participants, so it may appear that they can communicate with each other directly. But you may need to consider a relatively complicated scenario. For example, a participant can send a message to a target participant only if the target participant is in online mode (which is the common scenario for a chat server). So, with your proposed architecture, if they try to communicate with each other, each of them needs to maintain the status of all other participants before sending a message. And if the number of participants keeps growing, can you imagine the complexity of the system?
So, a mediator can certainly help you deal with a scenario like this. Figure 21-6 and Figure 21-7 depict the scenario.
Case 1. Communication without a mediator.
Case 2. Communication with a mediator.
- 2.What are advantages of using mediator patterns?
You can reduce the complexity of objects’ communication in a system.
The pattern promotes loose coupling.
It reduces number of subclasses in the system.
You can replace “many-to-many” relationship with “one-to-many” relationships, so it is much easier to read and understand. (Consider our first illustration in this context). And as an obvious effect, maintenance becomes easy.
You can provide a centralized control through the mediator with this pattern.
In short, it is always our aim to remove tight coupling (among objects) from our code and this pattern scores high in this context.
- 3.What are the disadvantages of using mediator patterns?
In some cases, implementing the proper encapsulation is tricky.
The mediator object’s architecture may become complex if you put too much logic inside it. An inappropriate use of the mediator pattern may end up with a “God Class” antipattern. (You’ll learn about antipatterns in Chapter 28).
Sometimes maintaining the mediator becomes a big concern.
- 4.
If you need to add a new rule or logic, you can directly add it to the mediator. Is this correct?
Yes.
- 5.
I am finding some similarities in the facade pattern and the mediator pattern. Is this correct?
Yes. In his book Design Pattern for Dummies (Wiley Publishing, 2006), Steve Holzner mentions the similarity by describing the mediator pattern as a multiplexed facade pattern. In mediator, instead of working with an interface of a single object, you are making a multiplexed interface among multiple objects to provide smooth transitions.
- 6.
In this pattern, you are reducing the number of interconnections among various objects. What key benefits have you achieved due to this reduction?
More interconnections among objects can make a monolithic system where the system’s behavior is difficult to change (the system’s behavior is distributed among many objects). As a side effect, you may need to create many subclasses to bring those changes in the system.
- 7.
In the modified implementations, you are using Thread.Sleep(1000). What is the reason for this?
You can ignore that. I used it to mimic a real-life scenario. I assume that participants are posting messages after reading a message properly and this activity takes a minimum of 1 second.
- 8.
In some applications, I have seen the use of a concrete mediator only. Is this approach OK?
The mediator pattern does not restrict you to use only a concrete mediator. But I like to follow the experts’ advice that says, “programming to the supertype (abstract class/interface) is a better approach,” and it can provide more flexibility in the long run.
- 9.
Can I simply say that if a class simply calls methods from multiple objects, it is a mediator?
Not at all. The key purpose of a mediator is to simplify the complex communications among objects in a system. I suggest that you always keep in mind the GoF definition and the corresponding concepts.
- 10.
In the first implementation, both send methods (mediator and employees) are named sendMessage() but in the modified implementation, they are different—one is send() and the other is sendMessage(). Do I need to follow any specific naming convention?
No. Both are fine. It’s your choice.