Mediator Pattern
Introduction#
The Mediator Design Pattern is one of the widely used behavioral patterns in Java. It provides a centralized communication medium between different objects in a system. The Mediator pattern encapsulates the way disparate sets of objects interact and communicate with each other, allowing loose coupling between them.
In a typical Java application, objects interact with each other directly, which can lead to high coupling. The Mediator pattern helps to reduce this coupling by introducing a mediator object that acts as a communication hub between objects. This mediator object encapsulates the communication logic, and all objects interact with it instead of directly interacting with each other. This results in a more maintainable and scalable codebase.
The Mediator pattern is particularly useful in complex systems where multiple objects need to communicate with each other. It helps to reduce the complexity of the codebase and makes it easier to add new functionality without affecting existing code. In the following sections, we will delve deeper into the Mediator Design Pattern in Java, its purpose, and how it can be implemented in Java applications.
Understanding Mediator Design Pattern#
Mediator Design Pattern is a behavioral pattern used in software engineering. It is one of the design patterns that fall under the category of Object-Oriented Programming (OOP). The main purpose of the Mediator Design Pattern is to help reduce the coupling between different components of a system by providing a centralized communication medium.
In the Mediator Design Pattern, a mediator object is created to handle the communication between different objects. The mediator object acts as a central hub that receives messages from different objects and sends them to their intended recipients. This way, the objects in the system do not have to communicate with each other directly, which helps to reduce the coupling between them.
The Mediator Design Pattern is often used in complex systems where there are many objects that need to communicate with each other. By using a mediator object, the system can be designed in a way that is more maintainable and easier to understand.
The UML diagram for the Mediator Design Pattern consists of four main components: Mediator, Colleague, ConcreteMediator, and ConcreteColleague. The Mediator is an abstract class that defines the interface for communicating with the Colleague objects. The Colleague is also an abstract class that defines the interface for communicating with the Mediator object. The ConcreteMediator and ConcreteColleague classes are the concrete implementations of the Mediator and Colleague classes, respectively.
Overall, the Mediator Design Pattern is a useful tool for software engineers who want to design systems that are more maintainable and easier to understand. By reducing the coupling between different components of a system, the Mediator Design Pattern can help to improve the overall quality of the software.
Example#
Here's a simple example of the Mediator pattern in Java:
import java.util.ArrayList;
import java.util.List;
// Mediator interface
interface Mediator {
void sendMessage(String message, Colleague colleague);
}
// Colleague interface
interface Colleague {
void receiveMessage(String message);
void sendMessage(String message);
}
// Concrete Mediator
class ConcreteMediator implements Mediator {
private List<Colleague> colleagues;
public ConcreteMediator() {
this.colleagues = new ArrayList<>();
}
public void addColleague(Colleague colleague) {
colleagues.add(colleague);
}
@Override
public void sendMessage(String message, Colleague originator) {
for (Colleague colleague : colleagues) {
// Don't send the message back to the originator
if (colleague != originator) {
colleague.receiveMessage(message);
}
}
}
}
// Concrete Colleague
class ConcreteColleague implements Colleague {
private Mediator mediator;
private String name;
public ConcreteColleague(Mediator mediator, String name) {
this.mediator = mediator;
this.name = name;
mediator.addColleague(this);
}
@Override
public void receiveMessage(String message) {
System.out.println(name + " received message: " + message);
}
@Override
public void sendMessage(String message) {
System.out.println(name + " sending message: " + message);
mediator.sendMessage(message, this);
}
}
public class MediatorPatternExample {
public static void main(String[] args) {
ConcreteMediator mediator = new ConcreteMediator();
ConcreteColleague colleague1 = new ConcreteColleague(mediator, "Colleague 1");
ConcreteColleague colleague2 = new ConcreteColleague(mediator, "Colleague 2");
ConcreteColleague colleague3 = new ConcreteColleague(mediator, "Colleague 3");
colleague1.sendMessage("Hello, colleagues!");
colleague2.sendMessage("Hi there!");
}
}
In this example, ConcreteMediator acts as the mediator, and ConcreteColleague objects communicate with each other through the mediator. The Mediator interface declares the sendMessage method, which is implemented by ConcreteMediator to handle communication between colleagues. Colleagues, in this case, are instances of the ConcreteColleague class.
Mediator and Colleague Interfaces#
The Mediator Design Pattern facilitates communication between components of a system by defining a common mediator interface. The mediator interface acts as a communication channel between the components, allowing them to communicate without being dependent on each other directly. The Mediator pattern promotes loose coupling between the components of a system, which makes it easier to modify and maintain the system.
Mediator Interface#
The Mediator interface defines the methods that the components can use to communicate with each other. The Mediator interface is implemented by a concrete mediator class, which encapsulates the interaction logic between the components.
The Mediator interface typically includes methods such as send
, receive
, and register
. The send
method is used by a component to send a message to another component through the mediator. The receive
method is used by a component to receive a message from another component through the mediator. The register
method is used by a component to register itself with the mediator.
Colleague Interface#
The Colleague interface defines the methods that a component can use to communicate with the mediator. The Colleague interface is implemented by a concrete colleague class, which represents a component in the system.
The Colleague interface typically includes methods such as send
, receive
, and setMediator
. The send
method is used by a component to send a message to another component through the mediator. The receive
method is used by a component to receive a message from another component through the mediator. The setMediator
method is used by a component to set the mediator that it will use to communicate with other components.
The Mediator and Colleague interfaces are the key components of the Mediator Design Pattern. They enable components to communicate with each other without being dependent on each other directly. By promoting loose coupling between the components of a system, the Mediator Design Pattern makes it easier to modify and maintain the system.
Implementation of Mediator Pattern#
The Mediator Pattern is implemented by defining a mediator object that encapsulates the communication between the colleague objects. In this section, we will discuss the implementation of the Mediator Pattern in Java.
Concrete Mediator and Colleague Classes#
The mediator class is responsible for the communication between the colleague objects. It provides a common interface for the colleague objects to communicate with each other. The concrete mediator class implements the mediator interface and coordinates the communication between the colleague objects. The concrete colleague classes implement the colleague interface and communicate with each other through the mediator object.
Java Util Concurrent Executor#
Java provides the java.util.concurrent.Executor
interface to execute tasks asynchronously. The Executor
interface defines the execute()
method, which takes a Runnable
object as a parameter and executes it asynchronously. The Executor
interface can be extended to create custom executor classes.
Java also provides the java.util.concurrent.ExecutorService
interface, which extends the Executor
interface and provides additional methods for managing the execution of tasks. The ExecutorService
interface can be used to create thread pools and manage the execution of tasks in a multithreaded environment.
The java.util.concurrent.ScheduledExecutorService
interface extends the ExecutorService
interface and provides additional methods for scheduling tasks to run at a specified time or after a specified delay. The ScheduledExecutorService
interface can be used to schedule tasks to run periodically.
In summary, the Mediator Pattern can be implemented in Java by defining a mediator object that encapsulates the communication between the colleague objects. The concrete mediator class implements the mediator interface and coordinates the communication between the colleague objects. Java provides the java.util.concurrent.Executor
interface for executing tasks asynchronously and the java.util.concurrent.ExecutorService
and java.util.concurrent.ScheduledExecutorService
interfaces for managing the execution of tasks in a multithreaded environment.
Benefits of Mediator Pattern#
The Mediator Design Pattern provides several benefits that make it a useful tool for developers working with Java.
One of the primary benefits of the Mediator Pattern is that it promotes loose coupling between objects. By using a mediator object to manage communication between dependent objects, the objects themselves do not need to be tightly coupled. This reduces the complexity of dependency management and communication among participating objects.
Another benefit of the Mediator Pattern is that it makes code more reusable. Because objects are not tightly coupled, they can be reused in different contexts without requiring major refactoring. This means that developers can save time and effort by reusing code rather than having to write new code from scratch.
The Mediator Pattern also makes it easier to refactor code. Because communication between objects is managed by a mediator object, changes to the communication protocol can be made in a centralized location rather than having to be made across multiple objects. This makes it easier to make changes to the codebase without introducing bugs or breaking existing functionality.
Finally, the Mediator Pattern can help reduce the maintenance burden of a codebase. By promoting loose coupling and making code more reusable, the Mediator Pattern can help reduce the amount of code that needs to be maintained over time. This can make it easier for developers to manage and maintain a codebase, particularly as it grows in size and complexity.
Overall, the Mediator Pattern is a powerful tool for managing communication between objects in a Java codebase. By promoting loose coupling, reusability, and maintainability, it can help developers create more robust and scalable code that is easier to work with over time.
Real World Examples of Mediator Pattern#
The Mediator Design Pattern is used to reduce coupling among disparate sets of objects that communicate with each other. It is a behavioral pattern that promotes loose coupling by keeping objects from referring to each other explicitly and allowing them to communicate indirectly through a mediator object. Here are some real-world examples of how the Mediator Pattern can be used in Java:
Flight Landing System#
An airport control tower is an excellent example of the Mediator Pattern. The tower looks after which flight can land and take off, and all communications are done from the airplane to the control tower. The tower acts as the mediator between the flights and ensures that there are no collisions or accidents. If all flights had to interact with each other to find out which flight is going to land next, it would create chaos. However, by using a mediator, the flights only send their status to the tower, and the tower sends the signals to confirm which airplane can take-off or land.
Chat Application#
Another example of the Mediator Pattern is a chat application where multiple users can communicate with each other. In this case, the mediator acts as the central hub that receives messages from different users and relays them to the intended recipients. The mediator can also add users to the chat room and remove them when they leave.
In a Java Message Service (JMS) based chat application, the mediator can be implemented using a message broker that acts as a centralized hub for all the messages. Each user sends a message to the message broker, which then relays it to the intended recipient. The message broker can also keep track of the users who are currently online and offline and notify other users when a new user joins or leaves the chat room.
By using the Mediator Pattern, the chat application can achieve loose coupling between the users, and the mediator can act as a central point of control for all the communications. The users do not need to know about each other, and they can communicate through the mediator, which relays the messages to the intended recipients.
In summary, the Mediator Pattern is a powerful design pattern that can be used in many real-world scenarios to reduce coupling between objects and promote loose coupling. By using a mediator object, the objects can communicate indirectly, which can reduce the complexity of the system and make it more scalable and maintainable.
Comparing Mediator Pattern with Other Patterns#
Mediator vs Observer Pattern#
The Mediator and Observer patterns are both behavioral design patterns that are used to manage communication between objects. However, the Mediator pattern is more focused on managing communication between a group of objects, while the Observer pattern is more focused on managing communication between two objects.
The Mediator pattern is often used in situations where there are a large number of objects that need to communicate with each other, but doing so directly would result in tightly coupled code. In contrast, the Observer pattern is often used in situations where one object needs to be notified when another object changes state.
Mediator vs Facade Pattern#
The Mediator and Facade patterns are both used to simplify communication between objects, but they do so in different ways. The Mediator pattern is used to manage communication between a group of objects, while the Facade pattern is used to provide a simplified interface to a complex system.
The Mediator pattern is often used in situations where there are a large number of objects that need to communicate with each other, but doing so directly would result in tightly coupled code. In contrast, the Facade pattern is often used to provide a simplified interface to a complex system, hiding its complexity from clients.
While the Mediator pattern is focused on managing communication between objects, the Facade pattern is focused on providing a simplified interface to a complex system. As a result, the two patterns are often used together to simplify communication between objects and provide a simplified interface to a complex system.
Overall, the Mediator pattern is a powerful tool for managing communication between objects in a programming language like Java. When used in combination with other patterns like the Observer and Facade patterns, it can help to create more maintainable, loosely coupled code that is easier to understand and modify over time.
Conclusion#
In conclusion, the Mediator Design Pattern is a powerful solution for managing complex communication between multiple objects. By providing a centralized hub for communication, this pattern promotes loose coupling and flexibility within a system.
One of the key benefits of the Mediator Pattern is its ability to abstract away complex communication logic, simplifying the overall business logic of a system. This can lead to more efficient computation and easier maintenance over time.
In Java, the java.util.Timer
class can be a useful tool for implementing the Mediator Pattern, allowing for timed events and scheduling within a system. Additionally, the JTextField
and KeyEvent
classes can be used to handle user input and trigger events within a Mediator-based system.
Overall, the Mediator Design Pattern is a valuable tool for managing complex communication within a system. By promoting loose coupling and simplifying business logic, it can lead to more efficient computation and easier maintenance over time.