Mediator Design Pattern in Java

1. Definition

The Mediator Design Pattern provides a unified interface to a set of interfaces in a subsystem. This pattern defines an object that encapsulates how a set of objects interact, promoting loose coupling by preventing objects from referring to each other explicitly.

2. Problem Statement

How can we design a system where multiple classes need to interact, but without them being tightly coupled, leading to a tangled web of interactions that is hard to understand and maintain?

3. Solution

Introduce a central mediator object that encapsulates the interactions between the classes. Instead of classes communicating directly with each other, they communicate via the mediator. This centralizes external communications and decouples the classes from one another.

4. Real-World Use Cases

1. Air traffic control tower managing flights.

2. Chat room where participants send messages via a central server.

3. GUI where various components like buttons, text boxes interact via a form.

5. Implementation Steps

1. Create a Mediator interface that will define the contract for communication.

2. Concrete classes will communicate with the mediator interface.

3. Use the mediator object to communicate between the concrete classes.

6. Implementation

// Mediator Interface
interface Mediator {
    void sendMessage(String msg, Colleague colleague);
}

// Concrete Mediator
class ConcreteMediator implements Mediator {
    private List<Colleague> colleagues = new ArrayList<>();

    void register(Colleague colleague) {
        colleagues.add(colleague);
    }

    @Override
    public void sendMessage(String msg, Colleague colleague) {
        for (Colleague c : colleagues) {
            // message should not be relayed back to the sender
            if (c != colleague) {
                c.receive(msg);
            }
        }
    }
}

// Abstract colleague class
abstract class Colleague {
    protected Mediator mediator;

    public Colleague(Mediator m) {
        mediator = m;
    }

    public void send(String message) {
        mediator.sendMessage(message, this);
    }
    public abstract void receive(String message);
}

// Concrete Colleague
class UserColleague extends Colleague {
    public UserColleague(Mediator m) {
        super(m);
    }

    @Override
    public void receive(String message) {
        System.out.println("UserColleague Received: " + message);
    }
}

// Client
public class MediatorDemo {
    public static void main(String[] args) {
        ConcreteMediator mediator = new ConcreteMediator();

        UserColleague user1 = new UserColleague(mediator);
        UserColleague user2 = new UserColleague(mediator);

        mediator.register(user1);
        mediator.register(user2);

        user1.send("Hello from User 1!");
    }
}

Output:

UserColleague Received: Hello from User 1!

Explanation

The Mediator Pattern focuses on the interaction between objects. Instead of communicating directly with each other, objects use a mediator. In our example, the UserColleague class does not communicate directly with other users but sends messages via the ConcreteMediator. This decouples the classes and centralizes external communications.

7. When to use?

Use the Mediator Pattern when:

1. A set of objects communicate in well-defined but complex ways that are hard to understand.

2. Reusing an object is difficult because it communicates with many other objects.

3. A behavior that's distributed between several classes should be customizable without too much subclassing.


Comments