Mediator Design Pattern in Java: Complete Guide with Real-World Examples

Mediator Design Pattern in Java: Complete Guide with Examples

Mediator Design Pattern in Java: Complete Guide with Real-World Examples

Have you ever worked on a Java application where one class depends on several other classes, and those classes also depend on one another? At first, everything may look simple. As new features arrive, however, changing one class can require changes across the entire application.

The Mediator Design Pattern in Java helps solve this problem by introducing a central object that coordinates communication between related objects. Instead of knowing about one another directly, the participating objects communicate through the mediator.

In this guide, we will understand the pattern using a chat room example, implement it using Java interfaces and classes, explore a Spring Boot use case, compare it with Observer and Facade, and prepare for common Java design-pattern interview questions.

1. What Is the Mediator Design Pattern?

The Mediator pattern is a behavioral design pattern that defines an object responsible for coordinating how a group of objects interact with one another.

Without a mediator, each object may need references to several other objects to perform its work. A mediator reduces these direct dependencies by providing a common communication point.

For example, in a chat application, a user should not need to maintain references to every other user in the room. The user sends a message to the chat room, and the chat room determines how to distribute it.

In simple words: Mediator centralizes communication so that objects do not need to coordinate all their interactions themselves.

Key participants

  • Mediator: Defines the communication operations.
  • Concrete Mediator: Implements the coordination rules.
  • Colleague: A participating object that communicates through the mediator.
  • Client: Creates or wires the objects and the mediator together.

2. A Real-World Analogy: Airport Control Tower

Imagine several aircraft approaching an airport. If every aircraft independently coordinated all takeoffs and landings with every other aircraft, managing their interactions would become difficult.

An airport control tower coordinates clearances and provides relevant instructions. Aircraft communicate with the tower rather than relying on direct coordination between every aircraft.

In software, a mediator serves a similar coordination role. The participating objects perform their own responsibilities while the mediator manages the interactions between them.

This is an analogy for the communication structure, not a claim that real-world air traffic control is implemented exactly like a software design pattern.

Where can this idea be applied?

  • A chat room that distributes messages among connected users.
  • A form controller that coordinates multiple user-interface fields.
  • A checkout workflow that coordinates inventory, payment, and order creation.
  • A workflow coordinator that manages interactions among application services.

3. The Problem Without the Mediator Pattern

Consider an online shopping application with four components: OrderService, PaymentService, InventoryService, and NotificationService.

When a customer places an order, the order component might need to coordinate payment, inventory, and notification operations. If those components also start calling one another directly, the dependency structure can become difficult to understand.

OrderService
   |--------> PaymentService
   |--------> InventoryService
   |--------> NotificationService

PaymentService
   |--------> NotificationService

InventoryService
   |--------> NotificationService

This is not automatically bad design. Direct dependencies can be perfectly reasonable in a small, clear workflow. The problem appears when the number of cross-component interactions grows and the same coordination logic gets scattered across multiple classes.

Common symptoms

  • Many classes hold references to several other classes.
  • Changing a workflow requires editing multiple components.
  • Coordination rules are duplicated in different places.
  • Unit tests need many dependencies just to test one interaction.
  • It is difficult to identify where the overall workflow is controlled.

The Mediator pattern can help when the real problem is complicated communication among related objects. It should not be added merely because a system has several classes.

4. How the Mediator Pattern Works

The general flow is straightforward:

  1. The client creates a mediator and the participating objects.
  2. Each participating object receives a reference to the mediator.
  3. An object reports an action or sends a request through the mediator.
  4. The mediator decides which participating objects should receive the request.
  5. The relevant objects perform their own responsibilities.

The important point is that a colleague does not need to know every other colleague's implementation details. It knows the mediator contract and uses that contract to request coordination.

What changes after introducing a mediator?

The direct connections between participating objects are reduced. Their interaction logic becomes easier to locate because the mediator provides a central place for the rules governing their communication.

This does not mean all business logic should move into the mediator. A well-designed mediator coordinates interactions and delegates the actual work to the appropriate components.

5. Mediator Design Pattern Diagram

This original diagram illustrates the difference between direct communication and mediator-based communication.

Mediator Design Pattern Architecture Three user objects communicate through a central chat mediator. The mediator routes messages to the intended recipients, so users do not need direct references to one another. User A Colleague User B Colleague ChatMediator Coordinates messages send deliver User C Colleague Key idea Colleagues communicate through the mediator, not through direct references to every other colleague.
Figure: A central mediator coordinates communication among participating objects.

6. Complete Java Implementation: Chat Room Example

A chat room is a useful way to understand the pattern because users need to send messages to one another without managing all the connections themselves.

We will implement four classes or interfaces:

  • ChatMediator — the mediator interface.
  • ChatRoom — the concrete mediator.
  • User — the colleague abstraction.
  • ChatRoomDemo — the client that wires everything together.

Step 1: Create the ChatMediator interface

public interface ChatMediator {

    void addUser(User user);

    void sendMessage(String message, User sender);
}

The interface defines two operations: registering a user and coordinating message delivery. User objects depend on this contract rather than on the concrete chat room implementation.

Step 2: Create the User class

public class User {

    private final String name;
    private final ChatMediator mediator;

    public User(String name, ChatMediator mediator) {
        this.name = name;
        this.mediator = mediator;
    }

    public String getName() {
        return name;
    }

    public void send(String message) {
        System.out.println(name + " sends: " + message);
        mediator.sendMessage(message, this);
    }

    public void receive(String message, String senderName) {
        System.out.println(
            name + " received from " + senderName + ": " + message
        );
    }
}

Notice that a user has a reference to the mediator, not a collection of all the other users. When the user sends a message, it delegates message distribution to the mediator.

Step 3: Implement the concrete ChatRoom mediator

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

public class ChatRoom implements ChatMediator {

    private final List<User> users = new ArrayList<>();

    @Override
    public void addUser(User user) {
        users.add(Objects.requireNonNull(user));
    }

    @Override
    public void sendMessage(String message, User sender) {
        Objects.requireNonNull(message);
        Objects.requireNonNull(sender);

        for (User user : users) {
            if (user != sender) {
                user.receive(message, sender.getName());
            }
        }
    }
}

The chat room maintains the registered users and distributes a message to everyone except the sender. The rule for message distribution lives in one place instead of being repeated in every user object.

This simple implementation is intended for a single-threaded demo. A production chat system would also need decisions about thread safety, user identity, message validation, delivery guarantees, disconnected users, and access control.

Step 4: Create the client

public class ChatRoomDemo {

    public static void main(String[] args) {

        ChatMediator chatRoom = new ChatRoom();

        User aman = new User("Aman", chatRoom);
        User priya = new User("Priya", chatRoom);
        User rahul = new User("Rahul", chatRoom);

        chatRoom.addUser(aman);
        chatRoom.addUser(priya);
        chatRoom.addUser(rahul);

        aman.send("Hello everyone!");
        System.out.println();

        priya.send("Hi Aman!");
    }
}

The client creates the mediator, creates the users with that mediator, registers them, and then sends messages. The users do not need references to one another.

7. Expected Output

Aman sends: Hello everyone!
Priya received from Aman: Hello everyone!
Rahul received from Aman: Hello everyone!

Priya sends: Hi Aman!
Aman received from Priya: Hi Aman!
Rahul received from Priya: Hi Aman!

The output demonstrates the mediator's role. Aman sends a message through the chat room, and the chat room distributes it to Priya and Rahul. The sender does not receive their own message in this implementation.

How the responsibilities are separated

Component Responsibility
ChatMediator Defines the communication contract.
ChatRoom Registers users and routes messages.
User Sends messages and handles received messages.
ChatRoomDemo Creates and connects the participating objects.

This separation makes the example easier to extend. For example, the mediator could later route messages to a specific user or group, provided those behaviors are added to the communication contract and implemented consistently.

8. Mediator Design Pattern in Spring Boot

In Spring Boot, the same coordination idea can be useful when several components participate in one workflow. Consider an order-placement process that needs to coordinate inventory reservation, payment authorization, and notification.

The following example shows an application-level coordinator. It is deliberately small so that the responsibility of the mediator remains clear.

Define the collaborating services

public interface InventoryService {
    void reserve(String orderId);
}
public interface PaymentService {
    void authorize(String orderId);
}
public interface NotificationService {
    void sendOrderConfirmation(String orderId);
}

Create the order workflow mediator

import org.springframework.stereotype.Component;

@Component
public class OrderWorkflowMediator {

    private final InventoryService inventoryService;
    private final PaymentService paymentService;
    private final NotificationService notificationService;

    public OrderWorkflowMediator(
            InventoryService inventoryService,
            PaymentService paymentService,
            NotificationService notificationService) {
        this.inventoryService = inventoryService;
        this.paymentService = paymentService;
        this.notificationService = notificationService;
    }

    public void placeOrder(String orderId) {

        inventoryService.reserve(orderId);

        paymentService.authorize(orderId);

        notificationService.sendOrderConfirmation(orderId);
    }
}

Call the mediator from an application service

import org.springframework.stereotype.Service;

@Service
public class OrderApplicationService {

    private final OrderWorkflowMediator workflowMediator;

    public OrderApplicationService(
            OrderWorkflowMediator workflowMediator) {
        this.workflowMediator = workflowMediator;
    }

    public void placeOrder(String orderId) {
        workflowMediator.placeOrder(orderId);
    }
}

The application service delegates the workflow to one coordinator. The coordinator knows which operations to invoke and in what order. Each underlying service remains responsible for its own operation.

Is this always the best design?

No. A class that simply calls three services in sequence may be a reasonable application service or workflow orchestrator; whether calling it a mediator is useful depends on the broader interaction design. Do not add a pattern name just to make the architecture look more sophisticated.

In a production order workflow, inventory reservation and payment authorization also require explicit failure handling, idempotency, transaction boundaries, and compensation or recovery logic. A mediator does not automatically provide these guarantees.

9. Mediator Pattern in Microservices

The mediator idea can be applied to application components, but it should not be confused with a message broker or an entire microservices architecture.

Imagine an order workflow involving several independent services:

  1. The order workflow begins after a customer places an order.
  2. Inventory is reserved.
  3. Payment is authorized.
  4. The order status is updated.
  5. A notification is sent.

A coordinator may manage the sequence of operations. In a distributed system, the implementation could use synchronous service calls, asynchronous messaging, or a durable workflow engine depending on the reliability and scaling requirements.

Application mediator vs message broker

Application mediator Message broker
Coordinates interactions defined in application code. Transports and routes messages between producers and consumers.
May invoke collaborating services directly. May provide queues, topics, acknowledgements, and delivery-related capabilities.
Does not inherently provide durable messaging. Durability and delivery behavior depend on the broker and its configuration.

In a Spring Boot application, a mediator-like coordinator might call application services. Kafka, RabbitMQ, or another messaging system can be used for asynchronous communication when the architecture needs it. These technologies solve related but different problems.

10. Advantages and Disadvantages of the Mediator Pattern

Advantages

  • Reduced direct coupling: Colleagues do not need to maintain references to every other participating object.
  • Centralized coordination: Interaction rules are easier to find and review.
  • Easier maintenance: Changes to interaction rules can often be made in the mediator instead of multiple colleagues.
  • Improved testability: Collaborators can be tested against a mediator interface or with mocked dependencies.
  • Flexible interactions: The mediator can determine which objects participate in a particular interaction.

Disadvantages

  • God object risk: A mediator can grow into a large class that knows too much about every part of the application.
  • More indirection: Understanding a simple interaction may require navigating through an additional class.
  • Complex coordination logic: If the mediator contains many branching rules, it can become difficult to test and maintain.
  • Potential runtime overhead: Additional delegation can introduce some overhead, though design clarity is usually the more important consideration.

The best mediator has a clear scope. If it begins handling unrelated workflows, business rules, persistence, retries, and notification formatting, consider splitting those responsibilities into separate components.

11. Mediator vs Observer vs Facade

These patterns can appear similar because all can reduce dependencies or simplify communication. Their intentions, however, are different.

Mediator vs Observer

Mediator Observer
Coordinates interactions among a group of objects. Notifies interested observers when a subject changes or emits an event.
The mediator decides how a request should be coordinated. The subject publishes a notification and observers react.
Useful for workflow and interaction coordination. Useful for event notification and subscriptions.

For example, a chat room that decides how to distribute a message illustrates Mediator. A component that notifies multiple listeners when its state changes illustrates Observer.

Mediator vs Facade

Mediator Facade
Coordinates communication among participating objects. Provides a simplified interface to a complex subsystem.
Colleagues typically use the mediator to coordinate interactions. Clients use the facade to access subsystem functionality more easily.
Focuses on collaboration and interaction rules. Focuses on simplifying access to a subsystem.

Mediator vs Strategy

Mediator Strategy
Coordinates communication among objects. Encapsulates interchangeable algorithms.
Useful when object interactions become complicated. Useful when a behavior can be implemented in different ways.
Example: routing messages among chat users. Example: selecting a different payment fee calculation algorithm.

12. Mediator Pattern and SOLID Principles

The Mediator pattern can support cleaner object-oriented design, especially when direct dependencies between colleagues are becoming difficult to manage. It does not automatically make an application SOLID, but it can help when applied carefully.

Single Responsibility Principle (SRP)

A mediator can own the coordination rules while each colleague remains responsible for its own behavior. Avoid moving every business rule into the mediator, or it may become responsible for too much.

Open/Closed Principle (OCP)

Depending on an interface such as ChatMediator allows a colleague to work with different mediator implementations without depending directly on a concrete class. New behavior may still require changes to the mediator contract or implementation.

Dependency Inversion Principle (DIP)

High-level components can depend on a mediator abstraction instead of a concrete mediator. This makes dependencies easier to replace in tests and alternate configurations.

Practical takeaway

Use the mediator to reduce unnecessary knowledge between collaborating objects, while keeping each component's own responsibilities clear. A central coordinator should not become a place where every unrelated responsibility is collected.

13. Best Practices for Experienced Java Developers

  1. Keep the mediator focused. Give it a clear coordination responsibility rather than making it the owner of all business logic.
  2. Use interfaces when they add value. A mediator interface is helpful when multiple implementations, test doubles, or clear abstraction boundaries are needed.
  3. Make dependencies explicit. Constructor injection is a good default for required collaborators in Spring Boot applications.
  4. Define failure behavior. Decide what should happen if one collaborator fails or returns an unsuccessful result.
  5. Test the coordination rules. Verify which collaborators are called, in what order, and how the mediator responds to failures.
  6. Do not confuse coordination with orchestration infrastructure. A simple Java mediator does not provide durable workflows, distributed transactions, retries, or message delivery guarantees automatically.
  7. Choose asynchronous messaging deliberately. Use events or a message broker when asynchronous decoupling, independent processing, or durable messaging is actually required.
  8. Watch for the God object problem. If one mediator grows too large, split it by workflow or responsibility.
  9. Do not introduce unnecessary abstraction. If two objects have a simple, stable relationship, direct communication may be clearer.

14. Java Interview Questions and Answers

Q1. What is the Mediator Design Pattern?

It is a behavioral design pattern that encapsulates how a group of objects interact, reducing the need for direct references between those objects.

Q2. Why is Mediator called a behavioral pattern?

It focuses on how objects communicate and collaborate rather than on how objects are created or how their structure is composed.

Q3. What problem does the Mediator pattern solve?

It helps manage complicated interactions between related objects by moving coordination rules into a dedicated mediator.

Q4. What are the main participants in the Mediator pattern?

The main participants are the mediator interface, concrete mediator, colleagues that communicate through it, and the client that wires the objects together.

Q5. Give a real-world example of Mediator in Java.

A chat room is a common example. Users send messages through the chat mediator, which decides which other users should receive them.

Q6. How does Mediator reduce coupling?

Participating objects depend on the mediator instead of holding direct references to every other participating object. This reduces the number of direct dependencies between colleagues.

Q7. What is the biggest disadvantage of the Mediator pattern?

The mediator can become a God object if it accumulates too many responsibilities or complex business rules.

Q8. What is the difference between Mediator and Observer?

Mediator coordinates interactions among objects. Observer focuses on notifying subscribed observers when a subject changes or emits an event. Both can be used together in some designs.

Q9. What is the difference between Mediator and Facade?

Mediator manages interactions between colleagues, whereas Facade simplifies how clients access a subsystem.

Q10. Can Mediator be used with Spring Boot?

Yes. A Spring-managed coordinator can organize interactions among services. Whether it should be called a mediator, application service, or workflow orchestrator depends on its responsibilities and the design context.

Q11. Is Spring ApplicationEventPublisher the Mediator pattern?

Not exactly. Spring application events provide a mechanism for publishing events and notifying listeners. That is closer to an event-driven Observer-style relationship. Event handling may participate in a broader coordination design, but the mechanisms should not be treated as interchangeable.

Q12. Can a mediator be asynchronous?

Yes. A mediator can coordinate asynchronous operations, but the implementation must define how completion, errors, ordering, timeouts, and cancellation are handled.

Q13. Does Mediator eliminate all coupling?

No. It reduces direct coupling between colleagues but introduces dependencies on the mediator contract. The mediator itself also depends on the objects it coordinates.

Q14. When should you avoid the Mediator pattern?

Avoid it when interactions are already simple and easy to understand, or when the mediator would merely add another layer without reducing meaningful complexity.

Q15. How would you test a mediator?

Unit tests can verify that the mediator sends the right requests to collaborators, respects the expected sequence, and handles failures correctly. Collaborators can often be replaced with mocks or fakes.

15. Frequently Asked Questions

Is Mediator a creational, structural, or behavioral pattern?

Mediator is a behavioral design pattern because it deals with communication and coordination between objects.

Is the Mediator pattern part of the Gang of Four patterns?

Yes. Mediator is one of the behavioral patterns in the Gang of Four design-pattern catalog.

Can there be more than one mediator in an application?

Yes. An application can have separate mediators for different groups of objects or workflows. Keeping their responsibilities separate can prevent a single mediator from becoming too large.

Does Mediator mean all communication must be synchronous?

No. The pattern describes how interactions are coordinated, not whether the operations must complete synchronously. An implementation can coordinate synchronous calls or asynchronous operations.

Can a mediator use dependency injection?

Yes. In Spring Boot, constructor injection is commonly used to supply the collaborators that a mediator or workflow coordinator needs.

Is Mediator the same as an API Gateway?

No. An API Gateway handles concerns such as routing and cross-cutting API behavior at an application boundary. A mediator coordinates interactions among participating objects. A gateway may coordinate some operations, but the two concepts have different purposes.

16. Quick Revision Checklist

  • Mediator is a behavioral design pattern.
  • It encapsulates interaction rules between related objects.
  • Colleagues communicate through a mediator instead of knowing every other colleague.
  • A concrete mediator implements the coordination logic.
  • A chat room is a simple, practical example.
  • Mediator reduces direct coupling but does not eliminate all dependencies.
  • Observer focuses on notifications; Mediator focuses on coordination.
  • Facade simplifies access to a subsystem.
  • A mediator should not become a God object.
  • In Spring Boot, distinguish a mediator from a general application service or distributed workflow engine.

Conclusion

The Mediator Design Pattern is useful when multiple objects need to collaborate and their direct dependencies are becoming difficult to maintain. By moving interaction rules into a dedicated coordinator, the design can become easier to understand, test, and change.

The chat room example demonstrates the core idea: users send messages through a mediator instead of maintaining direct connections to every other user. The same principle can help coordinate related components in a Java or Spring Boot application.

The key is to apply the pattern where it solves a real design problem. Keep the mediator focused, let each collaborator own its own behavior, and avoid introducing unnecessary layers into simple workflows.

If you are preparing for Java interviews, practise implementing Mediator alongside Observer, Strategy, Chain of Responsibility, and Command. Understanding when each pattern is useful matters more than memorizing its definition.

No comments: