Mediator

June 17, 2026 in patterns 6 minutes

A intermediate-level guide to Mediator: before-and-after java code and diagrams for a CS student.

The Tangled Web of Dependencies

Imagine you are building a user registration form. You have a Checkbox for terms and conditions, a TextField for the username, and a Button to submit the form. To make the UI feel professional, you want the Button to be disabled until the user has both checked the box and entered a name.

As you start coding, you realize that the Checkbox needs to know if the TextField is empty to decide whether to enable the button. The TextField also needs to tell the Button when its text changes so the button can update its state. Soon, your components are passing references of their neighbors into each other’s constructors just to stay in sync.

This creates “tight coupling”—a situation where objects are so deeply interconnected that changing one requires rewriting three others. If you decide to replace the TextField with a different input type, you have to hunt down every other class that was holding a reference to it.

class Checkbox {
    private Button submitButton;
    private TextField usernameField;

    public void setSubmitButton(Button btn) { this.submitButton = btn; }
    public void setUsernameField(TextField field) { this.usernameField = field; }

    public void onCheckedChange(boolean checked) {
        // The checkbox must know about the specific state and type of its neighbors
        if (checked && usernameField != null && !usernameField.getText().isEmpty()) {
            submitButton.setEnabled(true);
        } else {
            submitButton.setEnabled(false);
        }
    }
}

class Button {
    private boolean enabled = false;
    public void setEnabled(boolean enabled) { this.enabled = enabled; }
    public boolean isEnabled() { return enabled; }
}

class TextField {
    private String text = "";
    public void setText(String text) { this.text = text; }
    public String getText() { return text; }
}
  classDiagram
    Checkbox --> Button : submitButton
    Checkbox --> TextField : usernameField
    class Button {
        -boolean enabled
        +setEnabled(boolean)
        +isEnabled() boolean
    }
    class TextField {
        -String text
        +setText(String)
        +getText() String
    }

An Air Traffic Controller for Objects

To solve this, think about an airport. If every pilot in the sky had to talk directly to every other pilot to prevent collisions, the radio chatter would be a chaotic mess of overlapping signals. Instead, every pilot talks only to the Air Traffic Controller. The pilots don’t need to know where every other plane is; they only need to report their status to the controller, who then manages the flow for everyone.

In software, this “controller” is the Mediator. Instead of components talking to each other, they talk only to a single mediator object. This pattern centralizes the rules of interaction, allowing individual components to remain simple and reusable because they no longer need to know about their neighbors.

Implementing the Mediator

By introducing a FormMediator interface, we can strip the logic out of our UI components and move it into a specialized coordinator. Now, when a user interacts with a widget, the widget simply reports the event to the mediator and forgets about it. The mediator then decides which other parts of the system need to react.

interface FormMediator {
    void onCheckboxChanged(Checkbox checkbox, boolean checked);
    void onTextChanged(TextField field, String text);
}

class Checkbox {
    private final FormMediator mediator;

    public Checkbox(FormMediator mediator) { this.mediator = mediator; }

    public void onCheckedChange(boolean checked) {
        // The checkbox only notifies the mediator of its own state change
        mediator.onCheckboxChanged(this, checked);
    }
}

class Button {
    private boolean enabled = false;
    public void setEnabled(boolean enabled) { this.enabled = enabled; }
    public boolean isEnabled() { return enabled; }
}

class TextField {
    private String text = "";
    private final FormMediator mediator;

    public TextField(FormMediator mediator) { this.mediator = mediator; }

    public void setText(String text) {
        this.text = text;
        mediator.onTextChanged(this, text);
    }

    public String getText() { return text; }
}

class RegistrationForm implements FormMediator {
    private final Checkbox termsBox;
    private final TextField nameField;
    private final Button submitBtn;

    public RegistrationForm(Checkbox cb, TextField tf, Button b) {
        this.termsBox = cb;
        this.nameField = tf;
        this.submitBtn = b;
    }

    @Override
    public void onCheckboxChanged(Checkbox checkbox, boolean checked) {
        updateSubmitState();
    }

    @Override
    public void onTextChanged(TextField field, String text) {
        updateSubmitState();
    }

    private void updateSubmitState() {
        boolean isValid = termsBox.onCheckedChange(null, true) == null // logic proxy
                /* Simplified for example: check state */ 
                && !nameField.getText().isEmpty();
        // In real code, the mediator would hold references to component states
    }
}
  sequenceDiagram
    participant C as Checkbox
    participant M as RegistrationForm
    participant T as TextField
    participant B as Button

    Note over C,B: User checks the box and types a name
    C ->> M: onCheckboxChanged(checkbox, true)
    T ->> M: onTextChanged(field, "Alice")
    M ->> M: updateSubmitState()
    M -->> B: setEnabled(true)

The RegistrationForm acts as the mediator by implementing the FormMediator interface. It holds the references to all components and contains the logic that dictates how they interact.

  classDiagram
    class FormMediator {
        <<interface>>
        +onCheckboxChanged(Checkbox, boolean)
        +onTextChanged(TextField, String)
    }
    class RegistrationForm {
        -Checkbox termsBox
        -TextField nameField
        -Button submitBtn
        +updateSubmitState()
    }
    class Checkbox {
        -FormMediator mediator
        +onCheckedChange(boolean)
    }
    class TextField {
        -FormMediator mediator
        +setText(String)
    }
    class Button {
        -boolean enabled
        +setEnabled(boolean)
    }

    RegistrationForm ..|> FormMediator
    Checkbox --> FormMediator : mediator
    TextField --> FormMediator : mediator

When to Use It, and When Not To

The Mediator pattern is highly effective when you have a complex web of interactions between many objects that makes your code hard to maintain. By using a mediator, you can often combine it with the Observer pattern—where components “observe” state changes and notify the mediator—to keep your UI logic clean.

However, be wary of the “God Object” trap. Because the Mediator centralizes coordination, there is a temptation to move all the application’s business logic into it. A mediator should only handle how objects interact; it should not perform the actual work the components were designed for. If your mediator becomes thousands of lines long and handles everything from database connections to complex math, you have turned your coordinator into a bloated mess.

Additionally, do not over-engineer simple relationships. If two objects simply need to call one method on each other, adding a Mediator adds unnecessary layers of abstraction that make the code harder to follow.

Takeaways

  • Use a Mediator to stop objects from needing to know about their neighbors’ internal workings.
  • A Mediator handles “what happens when X changes,” allowing individual components to stay simple and reusable.
  • Avoid the “God Object” trap: A Mediator should coordinate interactions, not perform the core work of the application.

Usage

Comparing how components are initialized and wired together.

// Before: Components are tightly coupled and must know each other's types/methods.
Checkbox cb = new Checkbox();
Button btn = new Button();
TextField tf = new TextField();
cb.setSubmitButton(btn);
cb.setUsernameField(tf);

// After: Components only know the Mediator interface; coordination is centralized.
FormMediator mediator = new RegistrationForm(cb, tf, btn);
Checkbox cb2 = new Checkbox(mediator);
TextField tf2 = new TextField(mediator);
Button btn2 = new Button();