Mediator pattern is a behavioral design pattern that allows loose coupling among objects by controlling communication between them through a mediator object. This pattern defines an object that encapsulates how a set of objects interact, providing a centralized control point for communication instead of each object directly communicating with the others.
The Mediator pattern promotes decoupling by ensuring that no objects are aware of the logic of the communication between them. Instead, they only communicate with the mediator, which handles the interactions and updates the objects as needed. This design pattern is useful when the communication logic between objects is complex, and the interaction of the objects must be tightly controlled to prevent them from being tightly coupled.
Example: Let’s consider an example of a chat application where multiple users can send and receive messages to/from each other. In this case, Mediator pattern can be used to simplify the process of managing the communication between users. The mediator object in this case is the chat room, which controls the communication between users. The users are the objects being mediated, each with their own send message method.
Here is an example implementation of this pattern:
// Interface for mediator
public interface ChatRoom {
void send(String message, User sender);
void addUser(User user);
}
// Concrete mediator
public class ChatRoomImpl implements ChatRoom {
private List<User> users;
public ChatRoomImpl() {
this.users = new ArrayList<>();
}
@Override
public void send(String message, User sender) {
for (User user : this.users) {
// Don't send the message back to the sender
if (!user.equals(sender)) {
user.receive(message);
}
}
}
@Override
public void addUser(User user) {
this.users.add(user);
}
}
// Interface for colleagues
public interface User {
void send(String message);
void receive(String message);
void setChatRoom(ChatRoom chatRoom);
}
// Concrete colleague
public class BasicUser implements User {
private ChatRoom chatRoom;
private String name;
public BasicUser(String name) {
this.name = name;
}
@Override
public void send(String message) {
this.chatRoom.send(message, this);
}
@Override
public void receive(String message) {
System.out.println(this.name + " received message: " + message);
}
@Override
public void setChatRoom(ChatRoom chatRoom) {
this.chatRoom = chatRoom;
this.chatRoom.addUser(this);
}
}
// Usage
public class Main {
public static void main(String[] args) {
ChatRoom chatRoom = new ChatRoomImpl();
User user1 = new BasicUser("John");
User user2 = new BasicUser("Jane");
user1.setChatRoom(chatRoom);
user2.setChatRoom(chatRoom);
// User1 sends a message to user2
user1.send("Hello user2!");
}
}
In this example, the ChatRoom interface is the mediator, which defines the send and addUser methods. The ChatRoomImpl class is the concrete implementation of the mediator, which maintains a list of users and mediates the communication between them.
The User interface is the colleague interface, which defines the send, receive, and setChatRoom methods. The BasicUser class implements the User interface and sends and receives messages through the mediator.
Using the Mediator pattern in this example simplifies the process of managing the communication between users. Without it, the User objects would have to know about each other and communicate directly, which would result in tight coupling and be difficult to maintain as the number of users grows.