Behavioral Patterns  «Prev  Next»
Lesson 10 Mediator: applicability
Objective Decide when to Use the Mediator Pattern.

When to Use the Mediator Pattern and Applicability

Demonstrating the Mediator Pattern in Java

To provide a concrete demonstration of the Mediator pattern, consider the scenario of a chat room where multiple users can send messages to each other. Instead of users communicating directly with one another, they will use a centralized chat mediator. This mediator will control the delivery of messages, ensuring that every user gets the message intended for them.
  1. Mediator Interface: This is the contract that our concrete mediator will implement.
    public interface ChatMediator {
        void sendMessage(String msg, User user);
        void addUser(User user);
    }
    
  2. Concrete Mediator: This class implements the mediator interface and provides the logic to mediate between the users.
    import java.util.ArrayList;
    import java.util.List;
    
    public class ChatRoomMediator implements ChatMediator {
        private List<User> users;
    
        public ChatRoomMediator() {
            this.users = new ArrayList<>();
        }
    
        @Override
        public void sendMessage(String msg, User user) {
            for (User u : users) {
                // Do not send the message to the sender
                if (u != user) {
                    u.receive(msg);
                }
            }
        }
    
        @Override
        public void addUser(User user) {
            this.users.add(user);
        }
    }
    
  3. Abstract Colleague (User): All users will be extended from this class.
    public abstract class User {
        protected ChatMediator mediator;
        protected String name;
    
        public User(ChatMediator mediator, String name) {
            this.mediator = mediator;
            this.name = name;
        }
    
        public abstract void send(String message);
        public abstract void receive(String message);
    }
    
  4. Concrete User: This represents the users in the chat room.
    public class ChatUser extends User {
        public ChatUser(ChatMediator mediator, String name) {
            super(mediator, name);
            mediator.addUser(this);
        }
    
        @Override
        public void send(String message) {
            System.out.println(this.name + " sends: " + message);
            mediator.sendMessage(message, this);
        }
    
        @Override
        public void receive(String message) {
            System.out.println(this.name + " receives: " + message);
        }
    }
    

Demonstration of Mediator Pattern

public class MediatorPatternDemo {
    public static void main(String[] args) {
        ChatMediator mediator = new ChatRoomMediator();
        
        User alice = new ChatUser(mediator, "Alice");
        User bob = new ChatUser(mediator, "Bob");
        User charlie = new ChatUser(mediator, "Charlie");

        alice.send("Hi there!");
        bob.send("Hello, Alice!");
    }
}

In this demonstration, when Alice sends a message, the ChatRoomMediator ensures that all other users in the chat room, except for Alice, receive the message. The Mediator pattern effectively decouples the interactions between users, centralizing the messaging logic within the ChatRoomMediator. The Java classes above showcase the foundational architecture of the Mediator pattern, illustrating how complex interactions can be coordinated and streamlined using a centralized mediator.
The central principle of the Mediator pattern hinges on introducing one or multiple intermediaries to facilitate interactions among various components. Rather than establishing direct communication links, the components engage in dialogue through the Mediator, seeking its assistance for data retrieval or to broadcast updates. The purpose of instituting the Mediator pattern is to encapsulate the complex web of interactions among a collection of objects within a singular entity. By doing so, the Mediator fosters a loosely-coupled relationship among objects, eliminating the necessity for them to maintain explicit references to one another. This, in turn, affords flexibility in modifying their interactions without major disruptions. While decomposing a system into an assortment of objects has the potential to augment its reusability, an excessive network of interconnections may counteract this benefit. A dense mesh of relationships renders it improbable for an object to function autonomously, transforming the system into a seemingly indivisible entity. Furthermore, this intricacy can impose challenges in implementing substantial modifications to the system's functionality, as the behavior is dispersed across numerous objects. In such scenarios, the creation of numerous subclasses becomes an inevitable recourse to tailor the system’s behavior. Object-oriented design advocates for the dispersion of behavior across a network of objects. This approach, while beneficial, can inadvertently lead to a labyrinth of connections, culminating in a scenario where every object is aware of every other, which is an undesirable outcome. The Mediator pattern emerges as a strategic solution to this dilemma, streamlining interactions and simplifying the web of relationships.
The Mediator pattern is a good idea when:

  1. Changes in the state of one object affects many other objects.
  2. The sheer number of interconnections between objects makes the system unwieldy and hard to change.
  3. You want to be able to change the parts of a system independently from each other.

Figure: 3 Object Interaction: Mediator as a Communication Hub
Figure 3 - Object Interaction: Mediator as a Communication Hub.

In contrast to the diagram on the preceding page (see Figure2 on the previous page), the Mediator pattern can be used to design a controlled, coordinated communication model for a group of objects, eliminating the need for objects to refer to each other directly. The Mediator pattern suggests abstracting all object interaction details into a separate class, referred to as a Mediator, with knowledge about the interacting group of objects. Every object in the group is still responsible for offering the service it is designed for, but objects do not interact with each other directly for this purpose. The interaction between any two different objects is routed through the Mediator class and all objects send their messages to the mediator. The mediator then sends messages to the appropriate objects as per the requirements of the application. The resulting design has the following major advantages:
  1. With all the object interaction behavior moved into a separate (mediator) object, it becomes easier to alter the behavior of object interrelationships, by replacing the mediator with one of its subclasses with extended or altered functionality.
  2. Moving interobject dependencies out of individual objects results in enhanced object reusability.
  3. Because objects do not need to refer to each other directly, objects can be unit tested more easily.
  4. The resulting low degree of coupling allows individual classes to be modified without affecting other classes.