Mediator Pattern – Decoupling Object Communication

In software development, managing complex object interactions can be challenging. The Mediator pattern offers a solution by promoting loose coupling between objects, enhancing maintainability, and extensibility. In this blog post, we’ll explore the Mediator pattern in detail, including its use cases, benefits, and provide UML diagrams to aid understanding.

Statement

The Mediator pattern is a behavioral design pattern that promotes loose coupling between a set of objects by introducing a mediator object that encapsulates the communication logic between them. It allows objects to communicate with each other indirectly through the mediator, reducing the dependencies between them and making the system more maintainable and extensible.

In Mediator Design pattern, the objects that need to communicate with each other don’t have direct references to each other. Instead, they hold references to the mediator object. When an object wants to communicate with another object, it sends a message to the mediator, which then relays the message to the appropriate object or objects.

Use cases of Mediator Pattern

  1. Chat Application: In a chat application, multiple users need to communicate with each other. The Mediator pattern can be used to create a chat room mediator that manages the communication between users. Each user sends messages to the mediator, which then relays them to the appropriate recipients. This helps in organizing and facilitating communication among participants.
  2. Air Traffic Control System: In an air traffic control system, multiple aircraft need to coordinate their movements while avoiding collisions. The Mediator pattern can be employed to create a control tower mediator that handles communication between aircraft. The mediator receives messages from aircraft regarding their positions, intentions, and requests, and relays them to other aircraft, ensuring efficient and safe coordination.
  3. GUI Components: In a graphical user interface (GUI) application, various components like buttons, text fields, and checkboxes often need to interact with each other. The Mediator pattern can be utilized to create a mediator that handles the communication and coordination between these components. For example, when a button is clicked, it sends a message to the mediator, which then triggers appropriate actions in other components.
  4. Online Marketplace: In an online marketplace platform, there are multiple buyers, sellers, and products. The Mediator pattern can be applied by introducing a marketplace mediator that facilitates communication between buyers and sellers. When a buyer wants to purchase a product, they interact with the mediator, which then coordinates the negotiation, payment, and delivery processes between the relevant parties.
  5. Game Development: In a multiplayer game, players may need to interact with each other, exchange messages, or perform collaborative actions. The Mediator pattern can be used to create a game mediator that manages communication between players, handles game events, and facilitates gameplay interactions.

Implementation

Components of Mediator Pattern

  1. Mediator: This component defines the interface or abstract class that all concrete mediators must implement. It provides a common communication interface for the colleagues.
  2. Concrete Mediator: This component represents the actual mediator implementation. It implements the mediator interface and manages the communication and coordination between colleagues. It maintains references to the colleagues and controls the flow of communication among them.
  3. Colleague: This component represents the participants in the system that communicate through the mediator. It defines the interface or abstract class that all concrete colleagues must implement. It provides methods for sending and receiving messages through the mediator.
  4. Concrete Colleague: This component represents the actual colleague implementation. It implements the colleague interface and communicates with other colleagues through the mediator. It sends messages to the mediator, which then relays them to the appropriate recipients.

Java Example of Mediator Pattern

Here’s a Java example of an Air Traffic Control System using the Mediator design pattern:

// Mediator interface
interface ATCMediator {
    void registerFlight(Flight flight);
    void sendMessage(String message, Flight sender);
}

// Concrete mediator
class ATCControlCenter implements ATCMediator {
    private List<Flight> flights;

    public ATCControlCenter() {
        flights = new ArrayList<>();
    }

    @Override
    public void registerFlight(Flight flight) {
        flights.add(flight);
    }

    @Override
    public void sendMessage(String message, Flight sender) {
        for (Flight flight : flights) {
            if (flight != sender) {
                flight.receiveMessage(message);
            }
        }
    }
}

// Colleague class
class Flight {
    private String name;
    private ATCMediator mediator;

    public Flight(String name, ATCMediator mediator) {
        this.name = name;
        this.mediator = mediator;
    }

    public void sendMessage(String message) {
        System.out.println(name + " sends message: " + message);
        mediator.sendMessage(message, this);
    }

    public void receiveMessage(String message) {
        System.out.println(name + " receives message: " + message);
    }
}

// Example usage
public class Main {
    public static void main(String[] args) {
        ATCMediator controlCenter = new ATCControlCenter();

        Flight flight1 = new Flight("Flight 1", controlCenter);
        Flight flight2 = new Flight("Flight 2", controlCenter);
        Flight flight3 = new Flight("Flight 3", controlCenter);

        controlCenter.registerFlight(flight1);
        controlCenter.registerFlight(flight2);
        controlCenter.registerFlight(flight3);

        flight1.sendMessage("Flight 1 requesting landing clearance.");
        flight2.sendMessage("Flight 2 changing course.");
        flight3.sendMessage("Flight 3 experiencing turbulence.");

        /*
        Output:
        Flight 1 sends message: Flight 1 requesting landing clearance.
        Flight 2 receives message: Flight 1 requesting landing clearance.
        Flight 3 receives message: Flight 1 requesting landing clearance.
        Flight 2 sends message: Flight 2 changing course.
        Flight 1 receives message: Flight 2 changing course.
        Flight 3 receives message: Flight 2 changing course.
        Flight 3 sends message: Flight 3 experiencing turbulence.
        Flight 1 receives message: Flight 3 experiencing turbulence.
        Flight 2 receives message: Flight 3 experiencing turbulence.
        */
    }
}
Java

In this example, the ATCMediator interface defines the methods required for communication between flights. The ATCControlCenter class is the concrete mediator that implements the mediator interface and manages the communication between flights.

The Flight class represents the colleagues in the system. Each flight registers with the mediator and can send messages to other flights through the mediator.

In the Main class, we create an instance of the ATCControlCenter as the mediator. We then create three Flight instances and register them with the control center. Each flight can send messages using the sendMessage() method, which is relayed to other flights through the mediator.

When the flights send messages, they receive messages only from other flights, demonstrating the indirect communication facilitated by the mediator.

UML Diagram

         +-----------------+
         |   ATCMediator   |
         +-----------------+
         |                 |
         | +registerFlight()|
         | +sendMessage()  |
         +-----------------+

                  |
          +----------------------+
          |                     |
     +--------------------+     +------------------+
     | ATCControlCenter   |     |   Flight         |
     +--------------------+     +------------------+
     |                    |     |                  |
     | +registerFlight()  |     | +sendMessage().  |
     | +sendMessage()     |     | +receiveMessage()|
     +--------------------+     +------------------+
             ▲                         ▲
             |                         |
     +-------------------+     +-------------------------+
     | ConcreteColleague1|     | ConcreteColleague2      |
     +-------------------+     +-------------------------+
     | +sendMessage()    |     | +sendMessage()          |
     +-------------------+     +-------------------------+
UML

Pros & Cons of Mediator Pattern

Advantages of Mediator PatternDisadvantages of Mediator Pattern
Decoupling: The Mediator pattern promotes loose coupling between objects by removing direct references to each other. This enhances flexibility and makes it easier to modify and extend the system.Increased Complexity of the Mediator: The Mediator object can become complex as it takes on the responsibility of managing communication between multiple colleagues. This can lead to a large and potentially complex mediator object, which may be difficult to maintain or understand.
Simplified Communication: The Mediator pattern centralizes the communication logic in a mediator object, simplifying the interactions between objects. Colleagues only need to know about the mediator interface, reducing complexity and improving maintainability.Single Point of Failure: Since the Mediator pattern centralizes communication, the mediator itself becomes a single point of failure. If the mediator object encounters issues or fails, it can disrupt the entire system’s communication.
Enhanced Maintainability: With the Mediator pattern, the communication logic is concentrated in a single mediator object. This makes it easier to understand, modify, and debug the codebase, as changes to the communication logic can be made in one place.Performance Impact: Introducing a mediator can introduce additional overhead in the communication process. The mediator needs to handle routing and relaying messages, which can potentially impact the performance of the system, especially in highly dynamic or large-scale scenarios.
Extensibility: Adding new colleagues to the system is straightforward with the Mediator pattern. Since all communication goes through the mediator, new colleagues can be introduced without modifying existing colleagues or the mediator itself.Increased Dependencies on the Mediator: While the Mediator pattern reduces direct dependencies between objects, it introduces a new dependency on the mediator itself. Colleagues must rely on the mediator to facilitate their communication, which can introduce tight coupling between the mediator and colleagues.
Centralized Control: The Mediator pattern provides a centralized control point for managing interactions between objects. This can be particularly beneficial when there are complex coordination requirements or when certain actions need to be taken based on the state of multiple objects.

Mediator Pattern vs Other Patterns

Mediator vs Observer Pattern

The observer and mediator design patterns solve the same problem.

  • Purpose: The Mediator pattern focuses on decoupling and centralizing communication between objects. It promotes indirect communication through a mediator, reducing direct dependencies between objects. On the other hand, the Observer pattern focuses on establishing a one-to-many dependency between objects, where changes in one object trigger updates in dependent objects.
  • Communication: In the Mediator pattern, objects communicate through a mediator, while in the Observer pattern, objects directly observe and receive notifications from a subject.

Mediator vs Facade Pattern

Facade and Mediator have similar jobs: they try to organize collaboration between lots of tightly coupled classes.

But, Mediator pattern focuses on centralizing and coordinating communication between objects, promoting loose coupling and simplifying complex systems. On the other hand, the Facade pattern focuses on providing a simplified interface to a complex subsystem, shielding clients from its complexities.

The Mediator pattern deals with managing object interactions, while the Facade pattern deals with simplifying the usage of a system or subsystem.

Mediator vs Adapter Pattern

Mediator does not alter the messages received from objects, it just “mediates”, while Adapter Pattern can alter the messages.

Other Reads: https://www.scaler.com/topics/design-patterns/mediator-design-pattern/