Java implementation of the Mediator Design Pattern

The mediator design pattern is a type of behavioral design pattern, focusing on the actions of objects. This pattern is employed to establish a centralized means of communication between various objects within a system.

The Mediator Design Pattern is to be paraphrased natively. Just one option is needed.

Native version: The design pattern known as Mediator

The intention of the mediator pattern, as stated by the Gang of Four, is:

Enabling loose coupling, it involves encapsulating the manner in which different groups of objects interact and communicate, allowing each set of objects to function independently from one another.

The Mediator design pattern is extremely beneficial in enterprise applications where multiple objects are interacting with one another. If these objects interact directly, it creates a tightly-coupled system that is difficult and costly to maintain and extend. The Mediator pattern focuses on providing a mediator for communication between objects, facilitating loose coupling. A perfect example of this pattern is the air traffic controller, acting as a mediator for communication between different flights. The mediator serves as a router between objects, with its own logic for communication. The objects involved in communication are referred to as colleagues. Typically, an interface or abstract class defines the communication contract, followed by concrete implementations of mediators. In our example, we aim to implement a chat application where users can engage in group chats. Each user is identified by their name and can send and receive messages. All messages sent by any user should be received by all other users in the group.

Interface for the Mediator Pattern

To start with, let’s create a Mediator interface to establish the agreement for concrete mediators: ChatMediator.java.

 com.scdev.design.mediator;

public interface ChatMediator {

	public void sendMessage(String msg, User user);

	void addUser(User user);
}

Interface for Colleagues in the Mediator Pattern.

To enable the exchange of messages, it is possible to adopt either a User interface or an abstract class. I am choosing to implement User as an abstract class as demonstrated in the following User.java file.

 com.scdev.design.mediator;

public abstract class User {
	protected ChatMediator mediator;
	protected String name;
	
	public User(ChatMediator med, String name){
		this.mediator=med;
		this.name=name;
	}
	
	public abstract void send(String msg);
	
	public abstract void receive(String msg);
}

Please observe that the User possesses a mediator object reference, which is essential for facilitating communication between multiple users.

Native Paraphrase:
A mediator that is specific and tangible.

We will now proceed with the creation of a mediator class that specifically deals with concrete implementation. This class will hold a roster of users belonging to the group and will handle the communication logic between these users. Let’s name this class ChatMediatorImpl.java.

 com.scdev.design.mediator;

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

public class ChatMediatorImpl implements ChatMediator {

	private List<User> users;
	
	public ChatMediatorImpl(){
		this.users=new ArrayList<>();
	}
	
	@Override
	public void addUser(User user){
		this.users.add(user);
	}
	
	@Override
	public void sendMessage(String msg, User user) {
		for(User u : this.users){
			//message should not be received by the user sending it
			if(u != user){
				u.receive(msg);
			}
		}
	}

}

Concrete Colleague implementing the Mediator Design Pattern.

We are now able to generate concrete User classes that can be utilized by the client system. UserImpl.java has been created for this purpose.

 com.scdev.design.mediator;

public class UserImpl extends User {

	public UserImpl(ChatMediator med, String name) {
		super(med, name);
	}

	@Override
	public void send(String msg){
		System.out.println(this.name+": Sending Message="+msg);
		mediator.sendMessage(msg, this);
	}
	@Override
	public void receive(String msg) {
		System.out.println(this.name+": Received Message:"+msg);
	}

}

Take note that the send() function is utilizing a mediator to dispatch the message to the users, without any knowledge of how the mediator will manage it.

Code for a Client Program that demonstrates an example of the Mediator Pattern.

We can evaluate our chat application by using a basic program. In this program, we will establish a mediator and include users in the group. Furthermore, one of the users will send a message. The program file for this is called ChatClient.java.

 com.scdev.design.mediator;

public class ChatClient {

	public static void main(String[] args) {
		ChatMediator mediator = new ChatMediatorImpl();
		User user1 = new UserImpl(mediator, "Pankaj");
		User user2 = new UserImpl(mediator, "Lisa");
		User user3 = new UserImpl(mediator, "Saurabh");
		User user4 = new UserImpl(mediator, "David");
		mediator.addUser(user1);
		mediator.addUser(user2);
		mediator.addUser(user3);
		mediator.addUser(user4);
		
		user1.send("Hi All");
		
	}

}

Please observe that the client program is incredibly straightforward and lacks awareness of how the message is being managed and whether the mediator is receiving input from the user. The result of the example program utilizing the mediator pattern is as follows.

Pankaj: Sending Message=Hi All
Lisa: Received Message:Hi All
Saurabh: Received Message:Hi All
David: Received Message:Hi All

Class diagram for the Mediator Pattern.

An example of using the Mediator pattern in the JDK.

  • java.util.Timer class scheduleXXX() methods
  • Java Concurrency Executor execute() method.
  • java.lang.reflect.Method invoke() method.

Key points to consider about the Mediator Design Pattern are as follows:

  • Mediator pattern is useful when the communication logic between objects is complex, we can have a central point of communication that takes care of communication logic.
  • Java Message Service (JMS) uses Mediator pattern along with Observer pattern to allow applications to subscribe and publish data to other applications.
  • We should not use mediator pattern just to achieve lose-coupling because if the number of mediators will grow, then it will become hard to maintain them.

That concludes our discussion on the mediator design pattern and its implementation in Java.

 

More tutorials

The Command design pattern.(Opens in a new browser tab)

The Java language implements the Prototype Design Pattern.(Opens in a new browser tab)

Grafana Tutor Step-by-step Guide to Grafana on Ubuntu 18.04(Opens in a new browser tab)

Strategy Design Pattern in Java tutorial(Opens in a new browser tab)

Guidelines for Setting up SSL on Tomcat(Opens in a new browser tab)

Leave a Reply 0

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