Back to: Java Design Patterns
Mediator Design Pattern in Java with Examples
In this article, I am going to discuss the Mediator Design Pattern in Java with Examples. Please read our previous article where we discussed the Memento Design Pattern in Java with Examples. The Mediator Design Pattern falls under the category of Behavioral Design Pattern. In this article, we will explore the fundamental principles, advantages, and potential disadvantages of the Mediator design pattern, emphasizing its significance in facilitating communication and collaboration among objects.
What is a Mediator Design Pattern?
According to the Gang of Four definitions, define an object that encapsulates how a set of objects interact with each other. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.
In complex software systems, managing the interactions and dependencies between multiple objects can become challenging. The Mediator design pattern provides a solution by promoting loose coupling and centralizing communication through a mediator object. By abstracting and encapsulating the interactions between objects, the pattern simplifies the system’s architecture and enhances maintainability.
The Mediator design pattern is a behavioral pattern that defines an intermediary object, known as the mediator, to manage communication between multiple objects. The mediator encapsulates the interactions and dependencies between objects, reducing direct coupling and promoting loose coupling. Instead of objects communicating directly with each other, they communicate only through the mediator, enabling better control and coordination. The pattern consists of three primary components: the mediator interface, concrete mediators, and colleague objects. The mediator interface defines the communication protocol, and concrete mediators implement the mediation logic. The colleague objects, which interact with each other, are unaware of each other’s existence and rely on the mediator for communication.
Example to Understand Mediator Design Pattern:
Please have a look at the following diagram. As you can see in the below diagram, we have four objects (Object A, Object B, Object C, and Object D). And these four objects want to communicate with each other. Suppose, Object A wants to communicate with Object B, then Object A should know the reference of Object B, and using that reference Object A can call the method of Object B. Similarly if Object B wants to send some message to Object C, then it should know the reference of Object C and using that reference it will call the methods of Object C and sends the message.
The couplings between the objects are more i.e. tight coupling. A lot of object knows each other. Now in the above image, four objects are there. In real-time, you may have thousands of objects, and those thousands of objects want to communicate with each other. Then writing code and maintaining code is very difficult.
How do we Reduce the Coupling between the Objects?
Using Mediator Design Pattern, we can reduce the coupling between objects. To understand this, please have a look at the following diagram. As you can see in the below image, here we introduce the Mediator object. Each object has to send messages to the mediator object. What the mediator object does here is, it will receive the message from each object and route that message to the destination object. So, the mediator object will take care of handling the messages. So, in this type of scenario, we can use the Mediator Design Pattern.
Implementing Mediator Design Pattern in Java:
A real-world example where the Mediator design pattern can be applied is in a collaborative chat application. In this scenario, multiple users can communicate with each other through a chat interface, and there is a need for a centralized component that manages the communication between users.
The Mediator pattern can be used to implement the chat mediator, which acts as a centralized hub for routing messages between users. Instead of users directly interacting with each other, they send messages to the chat mediator, which then forwards the messages to the appropriate recipients.
The chat mediator encapsulates the communication logic and maintains the knowledge of active users and their connections. It coordinates the exchange of messages, ensures that messages reach the intended recipients, and handles any necessary transformations or validations. The UML Diagram of this example is given below using Mediator Design Pattern.
Step 1: Create a new directory to store all the class files of this project.
Step 2: Open VS Code and create a new project, called mediator.
Step 3: In the project, create a new file called User.java. Add the following code to the file:
public abstract class User { public abstract void sendMsg(String msg); public abstract void setName(String name); public abstract String getName(); }
This is the abstract class from which other concrete classes will extend.
Step 4: In the project, create three new files called UserA.java, UserB.java, and UserC.java. All of these classes implement the above User class. Add the following code to each of the files:
UserA.java
public class UserA extends User { private String name; private ChatRoom cr; public UserA (String name, ChatRoom cr) { this.name = name; this.cr = cr; } @Override public void sendMsg(String msg) {cr.showMsg(this, msg);} @Override public void setName(String name) {this.name = name;} @Override public String getName() {return name;} }
UserB.java
public class UserB extends User { private String name; private ChatRoom cr; public UserB(String name, ChatRoom cr) { this.name = name; this.cr = cr; } @Override public void sendMsg(String msg) {cr.showMsg(this, msg);} @Override public void setName(String name) {this.name = name;} @Override public String getName() {return name;} }
UserC.java
public class UserC extends User { private String name; private ChatRoom cr; public UserC (String name, ChatRoom cr) { this.name = name; this.cr = cr; } @Override public void sendMsg(String msg) {cr.showMsg(this, msg);} @Override public void setName(String name) {this.name = name;} @Override public String getName() {return name;} }
Step 5: In the project, create a new file called ChatRoom.java. Add the following code to the file:
import java.util.Date; public class ChatRoom { public void showMsg(User u, String msg) { System.out.print(new Date().toString() + "\t\t\t"); System.out.println(u.getName() + ": " + msg); } }
As can be seen, this class only contains a function that prints the message sent by a user to the terminal, along with the time of sending.
Step 6: In the project, create a new file called MediatorPatternDemo.java. This class will contain the main() function. Add the following code to MediatorPatternDemo.java:
public class MediatorPatternDemo { public static void main(String[] args) { ChatRoom cr = new ChatRoom(); User a = new UserA("Student A", cr); User b = new UserB("Student B", cr); User c = new UserC("Student C", cr); a.sendMsg("Hi Student B and Student C. This is Student A here!"); b.sendMsg("Hi Student A! How are you?"); c.sendMsg("Hi Student A! How are you?"); a.sendMsg("I am good! How are you guys?"); b.sendMsg("I'm fine!"); c.sendMsg("I'm also fine! :-)"); } }
The main function creates an object of type ChatRoom. This object represents the chat room to which all users post their messages to. The main function creates three users that will each send messages to the chat room.
Step 7: Compile and execute the application. Ensure compilation is successful. Verify that the program works as expected.
Congratulations! You now know how to implement mediator patterns!
UML Diagram of Mediator Design Pattern:
Now, let us see the Mediator Design Pattern UML Diagram Components with our Example so that you can easily understand the UML Diagram.
The classes can be described as follows:
- Object: This is the interface that will define the basic methods that will be implemented by the concrete object class.
- ConcreteObject: This class implements the Object interface. It implements the functions defined in the interface.
- AnotherObject: This object is another object that will be used by the main() function.
- DriverClass: This class contains the main() function and is responsible for the simulation of the program.
Advantages of Mediator Design Pattern in Java
The advantages of using the Mediator Design Pattern in Java are as follows:
- Decoupling and Centralization: The Mediator pattern promotes loose coupling between objects by removing direct dependencies. Colleague objects are only aware of the mediator and communicate through it, eliminating the need for direct communication and reducing the dependencies between objects. This decoupling enhances the flexibility and maintainability of the system, as changes to one object’s behavior or structure have minimal impact on other objects.
- Simplified Communication: The Mediator pattern simplifies communication and coordination between objects by providing a centralized point of control. Rather than each object needing to understand and interact with multiple other objects, they interact only with the mediator. The mediator handles the complexity of managing communication and ensures that the correct objects receive relevant information. This simplification improves the clarity and comprehensibility of the system’s interactions.
- Enhanced Reusability and Extensibility: The Mediator pattern facilitates reusability and extensibility by providing a single point of integration for new colleague objects. New objects can be easily integrated into the system by conforming to the mediator interface and leveraging the existing mediation logic. This reduces the effort required to incorporate new functionalities into the system and encourages modular design, making it easier to swap or add new colleague objects without affecting other components.
- Improved Maintainability: The Mediator pattern enhances the maintainability of the system by centralizing communication logic within the mediator. Changes to the communication protocol or business rules can be made in a single location, the mediator, rather than scattered across multiple objects. This centralization reduces the risk of introducing inconsistencies or errors and simplifies the task of understanding and modifying the system’s behavior.
- Mediation of Complex Interactions: The Mediator pattern is particularly useful in managing complex interactions and dependencies between objects. When multiple objects need to coordinate their actions or share information in a controlled manner, the mediator acts as a facilitator, ensuring that the interactions adhere to predefined rules. This simplifies the design and implementation of complex collaboration scenarios and promotes a structured and organized system architecture.
Disadvantages of Mediator Design Pattern in Java
The disadvantages of using the Mediator Design Pattern in Java are as follows:
- Increased Complexity of the Mediator: The Mediator pattern can introduce complexity, especially in scenarios with a large number of objects or complex communication requirements. The mediator object may become a central point of contention, responsible for handling and managing communication between multiple objects. As the complexity of the mediator increases, it may become harder to understand, maintain, and test. Care should be taken to strike a balance between centralizing communication and avoiding an overly complex mediator implementation.
- Potential Performance Overhead: The use of a mediator can introduce performance overhead, as all communication between objects needs to pass through the mediator. This indirect communication may introduce additional method calls, message passing, or event propagation, which can impact the overall system performance. While the performance impact is generally negligible in most cases, it is essential to assess the performance requirements of the system and consider potential optimizations if necessary.
- Limited Flexibility in Object Interaction: The Mediator pattern promotes a centralized communication model, where all interactions pass through the mediator. This can limit the flexibility of direct object-to-object communication, as all communication must go through the mediator, even for simple interactions. In scenarios where direct communication is necessary or beneficial, using the Mediator pattern may introduce unnecessary complexity and hinder efficiency.
- Mediator Becomes a Bottleneck: In systems with a high degree of object collaboration, the mediator may become a performance bottleneck or a single point of failure. If the mediator is overwhelmed with communication tasks or fails, it can adversely affect the entire system’s functionality. It is important to carefully design the mediator to handle communication efficiently and consider backup or redundancy mechanisms as critical to the system’s operation.
In the next article, I am going to discuss Observer Design Pattern in Java with Examples. Here, in this article, I try to explain Mediator Design Pattern in Java with Examples. I hope you understood the need for and use of the Mediator Design Pattern in Java.