Mediator Design Pattern in Java

In this article, we will do an in-depth exploration of Mediator Design Pattern in Java. So, let’s get started.

What is Mediator Design Pattern in Java ?

The Mediator Design Pattern in Java is a behavioral design pattern that facilitates communication and interaction between multiple objects by centralizing control through a mediator object.

The intermediate mediator object acts as a mediator between communicating objects thus removing the direct dependency between those communicating objects.It promotes loose coupling by keeping objects from referring to each other explicitly, and it allows their interaction to be varied independently.

Mediator Design Pattern in Java

Components: Mediator Design Pattern in Java

  • Mediator – defines an interface for communicating with Colleague objects.
  • ConcreteMediator – knows the colleague classes and keep a reference to the colleague objects.

– implements the communication and transfer the messages between the colleague classes

  • Colleague classes – keep a reference to its Mediator object

– communicates with the Mediator whenever it would have otherwise communicated with another Colleague.

Example: Mediator Design Pattern in Java

In the below example, we will use mediator pattern for implementing a chat room.

Class Diagram: Mediator Design Pattern in Java

Mediator Design Pattern in Java

Java Code

// Mediator Interface

package com.design.mediator;

public interface Mediator {

    public void sendMessage(String message, User fromUser);

    public void addUser(User user);

    public void removeUser(User user);

}

// ChatMediator Class

package com.design.mediator;

import java.util.ArrayList;
import java.util.List;

public class ChatMediator implements Mediator {

    private List users = new ArrayList < > ();

    @Override
    public void sendMessage(String message, User fromUser) {
        for (User user: users) {
            if (!user.equals(fromUser)) {
                user.receivedMessage(message);
            }
        }

    }

    public void addUser(User user) {
        users.add(user);
    }

    public void removeUser(User user) {
        users.remove(user);
    }

}

// User Interface

package com.design.mediator;

public interface User {

    public void sendMessage(String message);
    public void receivedMessage(String message);

}

// ChatUser Class

package com.design.mediator;

public class ChatUser implements User {

    private Mediator mediator;

    private String name;

    public ChatUser(Mediator mediator, String name) {
        this.mediator = mediator;
        this.name = name;
        mediator.addUser(this);
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        ChatUser other = (ChatUser) obj;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }

    public Mediator getMediator() {
        return mediator;
    }

    public String getName() {
        return name;
    }

    @Override
    public void sendMessage(String message) {
        System.out.println(name + ":" + "Sending Message:" + message);
        mediator.sendMessage(message, this);

    }

    @Override
    public void receivedMessage(String message) {
        System.out.println(name + ":" + "Received Message:" + message);

    }

}

// MediatorDemo Class

package com.design.mediator;

public class MediatorDemo {

    public static void main(String[] args) {
        Mediator mediator = new ChatMediator();
        User gyan = new ChatUser(mediator, "Gyan");
        new ChatUser(mediator, "Rochit");
        new ChatUser(mediator, "Vivek");
        gyan.sendMessage("Hi Guys");
    }

}

// Output
Gyan: Sending Message: Hi Guys
Rochit: Received Message: Hi Guys
Vivek: Received Message: Hi Guys

Use-Cases: Mediator Design Pattern in Java

  1. Chat Applications: Mediator pattern can be used to implement chat applications where users need to communicate with each other indirectly through a central chat room mediator.
  2. Air Traffic Control System: In a system managing air traffic, aircrafts can communicate with each other and the control tower through a mediator to avoid collisions and ensure orderly movement.
  3. GUI Components: Mediator can be used to coordinate interactions between various GUI components in a user interface, ensuring consistent and coordinated behavior.
  4. Distributed Systems: In distributed systems, where multiple components communicate over a network, a mediator can manage interactions and ensure proper synchronization.

Pros: Mediator Design Pattern in Java

  1. Reduced Coupling: Objects interact through a mediator, reducing direct coupling between components, making the system more maintainable and adaptable.
  2. Code Organization: Mediator encapsulates communication logic, leading to cleaner and more organized code.
  3. Flexibility: Adding or modifying components becomes easier as changes are localized to the mediator, without affecting other components.
  4. Centralized Control: Centralizes control and coordination, making it easier to manage complex interactions.
  5. Reusability: Mediator logic can be reused across different scenarios involving the same set of components.

Cons: Mediator Design Pattern in Java

  1. Complexity: Introducing a mediator might introduce additional complexity to the system, especially for simpler interactions.
  2. Single Point of Failure: The mediator becomes a single point of failure and potential performance bottleneck.
  3. Increased Dependencies: Components might become heavily dependent on the mediator, leading to its own set of challenges.

Best Practices: Mediator Design Pattern in Java

  1. Identify Dependencies: Identify components that need to communicate and determine the communication patterns between them.
  2. Define Mediator Interface: Create a mediator interface that declares methods for communication between components.
  3. Create Concrete Mediator: Implement the mediator interface with concrete mediator classes that manage communication and interactions.
  4. Component Registration: Components should register themselves with the mediator to establish communication.
  5. Keep Mediator Focused: Mediator should focus on communication and coordination only, avoiding other unrelated responsibilities.
  6. Use Observer Pattern: Often, the Observer pattern is used in conjunction with the Mediator pattern to notify components about changes.
  7. Clear Documentation: Document the roles and responsibilities of each component and the mediator to aid understanding and maintenance.
  8. Testing: Test the interactions between components through the mediator to ensure proper behavior.
  9. Avoid Overuse: Use the mediator pattern when the number of direct interactions becomes unmanageable, but avoid it for simple and straightforward scenarios.
  10. Standardize Communication: Define a consistent communication protocol and naming conventions to ensure clear and predictable interactions.

Conclusion: Mediator Design Pattern in Java

This article provided an in-depth exploration of the Mediator Design Pattern in Java, showcasing its significance in managing communication and interaction between objects. By utilizing a mediator object, the pattern promotes loose coupling, cleaner code organization, and enhanced flexibility in complex systems. Through real-world examples like chat applications and air traffic control systems, the article highlighted the practical applications of this pattern.

The article delved into the core components of the Mediator pattern, including the Mediator interface, concrete mediator implementations, and collaborating colleague classes. The presented Java code examples illustrated the step-by-step implementation of the Mediator Design Pattern, demonstrating how it can be applied to facilitate communication in different scenarios.

Use-cases such as chat applications, air traffic control systems, GUI components, and distributed systems highlighted the versatility of the Mediator pattern across various domains. The benefits of reduced coupling, centralized control, and code reusability were detailed in the pros section, while potential complexities and increased dependencies were noted as cons.

The best practices section emphasized the importance of identifying dependencies, defining clear interfaces, and maintaining a focused mediator object. By following these practices, developers can effectively harness the advantages of the Mediator Design Pattern while avoiding overuse and ensuring consistent communication protocols.

Must Read Design Pattern Articles: 

Recommended Books:

Leave a Reply

Your email address will not be published. Required fields are marked *