© Vaskaran Sarcar 2019
Vaskaran SarcarJava Design Patternshttps://doi.org/10.1007/978-1-4842-4078-6_21

21. Mediator Pattern

Vaskaran Sarcar1 
(1)
Bangalore, Karnataka, India
 

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

A common structure of the mediator pattern (which is basically adopted from the GoF’s Design Patterns: Elements of Reusable Object-Oriented Software) is often described with the diagram shown in Figure 21-1.
../images/395506_2_En_21_Chapter/395506_2_En_21_Fig1_HTML.jpg
Figure 21-1

Mediator pattern example

The participants are described as follows.
  • 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.

Employee is an abstract class and the JuniorEmployee and SeniorEmployee classes are the concrete implementations of it. The sendMessage() method of the Employee class is described as follows.
public void sendMessage(String msg) throws InterruptedException
{
    mediator.sendMessage(this, msg);
}

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

Figure 21-2 shows the class diagram.
../images/395506_2_En_21_Chapter/395506_2_En_21_Fig2_HTML.jpg
Figure 21-2

Class diagram

Package Explorer View

Figure 21-3 shows the high-level structure of the program.
../images/395506_2_En_21_Chapter/395506_2_En_21_Fig3_HTML.jpg
Figure 21-3

Package Explorer view

Implementation

Here’s the first implementation.
package jdp2e.mediator.demo;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
interface Mediator
{
    void register(Employee employee);
    void sendMessage(Employee employee, String msg) throws InterruptedException;
}
// ConcreteMediator
class ConcreteMediator implements Mediator
{
    List<Employee> participants = new ArrayList<Employee>();
    @Override
    public void register(Employee employee)
    {
        participants.add(employee);
    }
    public void displayRegisteredEmployees()
    {
        System.out.println("At present,registered employees are:");
        for (Employee employee: participants)
        {
            System.out.println(employee.getName());
        }
    }
    @Override
    public void sendMessage(Employee employee, String msg) throws InterruptedException
    {
        if (participants.contains(employee))
        {
            System.out.println(employee.getName() +" posts:"+ msg+"Last message posted at "+LocalDateTime.now());
            Thread.sleep(1000);
        }
        else
        {
            System.out.println("An outsider named "+ employee.getName()+" is trying to send some messages.");
        }
    }
}
// The abstract class-Employee
abstract class Employee
{
    protected Mediator mediator;
    protected String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    // Constructor
    public Employee(Mediator mediator)
    {
        this.mediator = mediator;
    }
    public void sendMessage(String msg) throws InterruptedException
    {
        mediator.sendMessage(this, msg);
    }
    public abstract String employeeType();
}
// Junior Employee
class JuniorEmployee extends Employee
{
    public JuniorEmployee(Mediator mediator, String name)
    {
        super(mediator);
        this.name = name;
    }
    @Override
    public String employeeType()
    {
        return "JuniorEmployee";
    }
}
//Senior Employee
class SeniorEmployee extends Employee
{
    // Constructor
    public SeniorEmployee(Mediator mediator, String name)
    {
        super(mediator);
        this.name = name;
    }
    @Override
    public String employeeType()
    {
        return "SeniorEmployee";
    }
}
// Unknown participant.
class Unknown extends Employee
{
    // Constructor
    public Unknown(Mediator mediator, String name)
    {
        super(mediator);
        this.name = name;
    }
    @Override
    public String employeeType()
    {
        return "Outsider";
    }
}
public class MediatorPatternExample {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("***Mediator Pattern Demo*** ");
        ConcreteMediator mediator = new ConcreteMediator();
        JuniorEmployee amit = new JuniorEmployee(mediator, "Amit");
        JuniorEmployee sohel = new JuniorEmployee(mediator, "Sohel");
        SeniorEmployee raghu = new SeniorEmployee(mediator, "Raghu");
        //Registering participants
        mediator.register(amit);
        mediator.register(sohel);
        mediator.register(raghu);
        //Displaying the participant's list
        mediator.displayRegisteredEmployees();
        System.out.println("Communication starts among participants...");
        amit.sendMessage("Hi Sohel,can we discuss the mediator pattern?");
        sohel.sendMessage("Hi Amit,yup, we can discuss now.");
        raghu.sendMessage("Please get back to work quickly.");
        //An outsider/unknown person tries to participate
        Unknown unknown = new Unknown(mediator, "Jack");
        unknown.sendMessage("Hello Guys..");
    }
}

Output

Here’s the output.
***Mediator Pattern Demo***
At present,registered employees are:
Amit
Sohel
Raghu
Communication starts among participants...
Amit posts:Hi Sohel,can we discuss the mediator pattern?Last message posted at 2018-09-09T17:41:21.868
Sohel posts:Hi Amit,yup, we can discuss now.Last message posted at 2018-09-09T17:41:23.369
Raghu posts:Please get back to work quickly.Last message posted at 2018-09-09T17:41:24.870
An outsider named Jack is trying to send some messages .

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

You have just seen a simple example of the mediator pattern. But you can make it better. You identified the following points.
  • 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.

So, keeping these points in mind, let’s modify the previous example. Here are some key characteristics of the modified implementation.
  • 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.

Employee Amit = new ConcreteEmployee(mediator, "Amit", true);
The third argument (true/false) is used to determine whether a participant wants to register himself or not to the mediator. He is treated accordingly when he tries to post messages.
  • 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

if( fromEmployee.employeeType()=="UnauthorizedUser")
you could directly use this line of code:
if( fromEmployee.getClass().getSimpleName().equals("UnauthorizedUser"))

I used the former one for better readability.

Modified Class Diagram

Figure 21-4 shows the modified class diagram. To show the key changes and to present a neat diagram, I do not show the client code dependencies in the following diagram.
../images/395506_2_En_21_Chapter/395506_2_En_21_Fig4_HTML.jpg
Figure 21-4

Class diagram

Modified Package Explorer View

Figure 21-5 shows the modified Package Explorer view.
../images/395506_2_En_21_Chapter/395506_2_En_21_Fig5_HTML.jpg
Figure 21-5

Modified Package Explorer view

Modified Implementation

Here is the modified implementation.
package jdp2e.mediator.modified.demo;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
interface Mediator
{
    void register(Employee employee);
    void sendMessage(Employee fromEmployee, Employee toEmployee,String msg) throws InterruptedException;
}
// ConcreteMediator
class ConcreteMediator implements Mediator
{
    List<Employee> participants = new ArrayList<Employee>();
    @Override
    public void register(Employee employee)
    {
        participants.add(employee);
    }
    public void displayRegisteredEmployees()
    {
        System.out.println("At present ,registered participants are:");
        for (Employee employee: participants)
        {
            System.out.println(employee.getName());
        }
    }
    @Override
    public void sendMessage(Employee fromEmployee,Employee toEmployee,String msg) throws InterruptedException
    {
        /*if( fromEmployee.getClass().getSimpleName().equals("UnauthorizedUser"))*/
        if( fromEmployee.employeeType()=="UnauthorizedUser")
        {
            System.out.println("[ALERT Everyone] An outsider named "+ fromEmployee.getName()+" trying to send some messages to "+ toEmployee.getName());
            fromEmployee.receive(fromEmployee, ",you are not allowed to enter here.");
        }
        else if (participants.contains(fromEmployee))
        {
            System.out.println("-----"+fromEmployee.getName() +" posts some message at: "+LocalDateTime.now()+"-----");
            Thread.sleep(1000);
            //No need to inform everyone or himself
            //Only let the target receiver know
            if(participants.contains(toEmployee))
            {
                toEmployee.receive(fromEmployee,msg);
            }
            //If target receipient does not exist
            else
            {
                System.out.println(fromEmployee.getName() +" , your target recipient does not exist");
            }
        }
        //An outsider tries to send message.
        else
        {
            System.out.println("[ALERT] An unregistered employee named "+ fromEmployee.getName()+" trying to send some messages to "+ toEmployee.getName());
            System.out.println(fromEmployee.getName()+", you need to register yourself first.");
        }
    }
}
// Employee
abstract class Employee
{
    private Mediator mediator;
    protected String name;
    private boolean authorizedUser;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    // Constructor
    public Employee(Mediator mediator, String name, boolean authorizedUser)
    {
        this.mediator = mediator;
        this.name=name;
        this.authorizedUser=authorizedUser;
        if(authorizedUser)
        {
            mediator.register(this);
        }
    }
    //The following method name need not be same as the Mediator's method name
    public void send(Employee toFriend,String msg) throws InterruptedException
    {
        mediator.sendMessage(this,toFriend, msg);
    }
    //public abstract void receive(Friend fromFriend,String message);
    public void receive(Employee fromFriend,String message)
    {
        System.out.println(this.name+" received a message : " + message +" from an employee "+ fromFriend.getName() +".");
    }
    public abstract String employeeType();
}
//A concrete friend
class ConcreteEmployee extends Employee
{
    public ConcreteEmployee(Mediator mediator, String name,boolean authorizedUser)
    {
        super(mediator,name, authorizedUser);
    }
    @Override
    public String employeeType()
    {
        return "ConcreteEmployee";
    }
}
//Unauthorized user
class UnauthorizedUser extends Employee
{
    public UnauthorizedUser(Mediator mediator, String name)
    {
        //The user is always treated an unauthorized user.So, the flag is
        //false always.
        super(mediator,name, false);
    }
    @Override
    public void receive(Employee fromEmployee,String message)
    {
        System.out.println(this.name + message);
    }
    @Override
    public String employeeType()
    {
        return "UnauthorizedUser";
    }
}
public class ModifiedMediatorPatternExample {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("***Mediator Pattern Demo*** ");
        ConcreteMediator mediator = new ConcreteMediator();
        Employee Amit = new ConcreteEmployee(mediator, "Amit", true);
        Employee Sohel = new ConcreteEmployee(mediator, "Sohel",true);
        Employee Raghu = new ConcreteEmployee(mediator, "Raghu",true);
        //Unauthorized user
        Employee Jack = new ConcreteEmployee(mediator, "Jack",false);
        //Only two parameter needed to pass in the following case.
        Employee Divya = new UnauthorizedUser(mediator, "Divya");
        //Displaying the participant's list
        mediator.displayRegisteredEmployees();
        System.out.println("Communication starts among participants...");
        Amit.send(Sohel,"Hi Sohel,can we discuss the mediator pattern?");
        Sohel.send(Amit,"Hi Amit,Yup, we can discuss now.");
        //Boss is sending messages to each of them individually
        Raghu.send(Amit,"Please get back to work quickly.");
        Raghu.send(Sohel,"Please get back to work quickly.");
        //An unregistered employee(Jack) and an outsider(Divya) are also
        //trying to participate.
        Jack.send(Amit,"Hello Guys..");
        Divya.send(Raghu, "Hi Raghu");
    }
}

Modified Output

Here is the modified output.
***Mediator Pattern Demo***
At present ,registered participants are:
Amit
Sohel
Raghu
Communication starts among participants...
-----Amit posts some message at: 2018-09-04T20:37:00.999-----
Sohel received a message : Hi Sohel,can we discuss the mediator pattern? from an employee Amit.
-----Sohel posts some message at: 2018-09-04T20:37:01.999-----
Amit received a message : Hi Amit,Yup, we can discuss now. from an employee Sohel.
-----Raghu posts some message at: 2018-09-04T20:37:03.002-----
Amit received a message : Please get back to work quickly. from an employee Raghu.
-----Raghu posts some message at: 2018-09-04T20:37:04.016-----
Sohel received a message : Please get back to work quickly. from an employee Raghu.
[ALERT] An unregistered employee named Jack trying to send some messages to Amit
Jack, you need to register yourself first.
[ALERT Everyone] An outsider named Divya trying to send some messages to Raghu
Divya,you are not allowed to enter here .

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. 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.

     
../images/395506_2_En_21_Chapter/395506_2_En_21_Fig6_HTML.jpg
Figure 21-6

Communication without using a mediator

../images/395506_2_En_21_Chapter/395506_2_En_21_Fig7_HTML.jpg
Figure 21-7

Communication using a mediator

Case 2. Communication with a mediator.

Also, you can consider the modified implementation in this context. In the modified implementation, you can see that the mediator is maintaining the logic—who should be allowed to post messages on the server and how he/she should be treated.
  1. 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.

     
  2. 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.

     
  3. 4.

    If you need to add a new rule or logic, you can directly add it to the mediator. Is this correct?

    Yes.

     
  4. 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.

     
  5. 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.

     
  6. 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.

     
  7. 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.

     
  8. 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.

     
  9. 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.

     
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset