1. Definition

The Mediator pattern defines an object that encapsulates how a set of objects interact. It promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.

2. Problem Statement

Consider a system with many components (like buttons, text boxes, etc.) in a UI. Each of these components should interact with others. Directly connecting every component to every other component would result in a tightly coupled system where each component knows about several other components, leading to a complex web of dependencies.

3. Solution

Instead of connecting components directly, you can have them communicate through a central mediator. When a component changes, it tells the mediator, which then handles the effect of this change on other components.

4. Real-World Use Cases

1. Air traffic control systems where the tower acts as a mediator coordinating flights.

2. Chat rooms where a central system manages messages and broadcasts them to all participants.

5. Implementation Steps

1. Create a Mediator interface or abstract class that outlines the communication required by components.

2. Create concrete mediator classes that coordinate communication between different components.

3. Components only communicate with the mediator instead of communicating directly with each other.

6. Implementation in Java

// Mediator interface
interface Mediator {
    void notify(Component sender, String event);
}

// Concrete mediator
class ConcreteMediator implements Mediator {
    private Button button;
    private TextBox textBox;

    public void setButton(Button button) {
        this.button = button;
    }

    public void setTextBox(TextBox textBox) {
        this.textBox = textBox;
    }

    @Override
    public void notify(Component sender, String event) {
        if (sender == button && event.equals("clicked")) {
            textBox.displayText("Button was clicked!");
        }
    }
}

// Abstract component
abstract class Component {
    protected Mediator mediator;

    public Component(Mediator mediator) {
        this.mediator = mediator;
    }

    public abstract void click();
}

// Concrete components
class Button extends Component {
    public Button(Mediator mediator) {
        super(mediator);
    }

    @Override
    public void click() {
        mediator.notify(this, "clicked");
    }
}

class TextBox extends Component {
    public TextBox(Mediator mediator) {
        super(mediator);
    }

    public void displayText(String text) {
        System.out.println(text);
    }

    @Override
    public void click() {
        // Do nothing for this example
    }
}

// Client
public class Client {
    public static void main(String[] args) {
        ConcreteMediator mediator = new ConcreteMediator();
        Button button = new Button(mediator);
        TextBox textBox = new TextBox(mediator);

        mediator.setButton(button);
        mediator.setTextBox(textBox);

        button.click();
    }
}

Output:

Button was clicked!

Explanation:

In this example, Button and TextBox are components that interact via the ConcreteMediator. When the button is clicked, instead of the button affecting the textbox directly, it informs the mediator. The mediator then tells the textbox to display a message. This way, the button and textbox are decoupled, and their interaction is centralized within the mediator.

7. When to use?

1. When you want to reduce direct communication between various classes or objects, promoting loose coupling.

2. When you want to centralize external communications.

3. When you want to make object interactions customizable without subclassing components.