Skip to content

Mediator Design Pattern (Behavioral)

Mediator is a behavioral design pattern that lets you reduce chaotic dependencies between objects. The pattern restricts direct communications between the objects and forces them to collaborate only via a mediator object.


Imagine you have a dialog for creating and editing customer profiles. It consists of various form controls such as text fields, checkboxes, and buttons. Some of these elements interact with each other. For instance, checking the β€œI have a dog” checkbox might reveal a hidden text field for entering the dog’s name. Another example is the β€œSubmit” button, which must validate the data in all fields before saving it.

By having these elements talk directly to each other, you create a web of dependencies. This makes the code difficult to maintain and reuse. If you want to use the same checkbox in another form, you have to bring along the text field and the button as well.

The Mediator pattern suggests that you should stop all direct communication between the components which you want to make independent of each other. Instead, these components must collaborate indirectly, by calling a special mediator object that redirects the calls to appropriate components.

As a result, the components depend only on a single mediator class instead of being coupled to dozens of their colleagues. In our dialog example, the dialog class itself may act as the mediator. It will know which components should be notified when a specific element is interacted with.

Air traffic controllers at an airport act as mediators. Pilots of aircraft approaching or departing the airport terminal area don’t communicate directly with each other. Instead, they speak to an air traffic controller, who sits in a tall tower and coordinates all the flights. Without the controller, pilots would have to be aware of every other plane in the vicinity, which would be chaotic and dangerous.


  1. Components are various classes that contain some business logic. Each component has a reference to a mediator, declared with the type of the mediator interface.
  2. Mediator Interface declares methods of communication with components, which usually include just a single notification method.
  3. Concrete Mediator encapsulates relations between various components. Concrete mediators often keep references to all components they manage and sometimes even manage their lifecycle.
  4. Communication Flow: Components notify the mediator about events. The mediator then decides which other components should handle the event and redirects the call.
classDiagram
    class Mediator {
        <<interface>>
        +notify(sender, event)
    }
    class ConcreteMediator {
        -componentA: ComponentA
        -componentB: ComponentB
        +notify(sender, event)
    }
    class BaseComponent {
        -mediator: Mediator
        +setMediator(mediator)
    }
    class ComponentA {
        +operationA()
    }
    class ComponentB {
        +operationB()
    }

    Mediator <|-- ConcreteMediator
    BaseComponent <|-- ComponentA
    BaseComponent <|-- ComponentB
    ConcreteMediator o-- ComponentA
    ConcreteMediator o-- ComponentB
    BaseComponent o-- Mediator

# The mediator interface declares a method used by components
# to notify the mediator about various events.
interface Mediator:
method notify(sender, event)
# The concrete mediator class.
class AuthenticationDialog implements Mediator:
private field title, loginOrRegisterChbox, loginUsername
# ... other fields
method notify(sender, event):
if (sender == loginOrRegisterChbox && event == "check"):
if (loginOrRegisterChbox.checked):
title = "Log in"
# show login fields
else:
title = "Register"
# show registration fields
# Components communicate with a mediator using its interface.
class BaseComponent:
protected field mediator: Mediator
constructor BaseComponent(mediator = null):
this.mediator = mediator
method click():
mediator.notify(this, "click")

  • Complex Dependencies: Use the Mediator pattern when it’s difficult to change some of the classes because they are tightly coupled to a bunch of other classes.
  • Reusability: Use the pattern when you can’t reuse a component in a different program because it’s too dependent on other components.
  • Subclass Proliferation: Use the Mediator when you find yourself creating tons of component subclasses just to reuse some basic behavior in various contexts.
  1. Identify a group of tightly coupled classes which would benefit from being more independent.
  2. Declare the mediator interface and describe the desired communication protocol between mediators and components.
  3. Implement the concrete mediator class. This class will store references to all the components it manages.
  4. Components should store a reference to the mediator object. They should notify the mediator instead of calling other components directly.
  5. The client must initialize the mediator and pass it to all components.

Pros:

  • Single Responsibility Principle: You can extract the communications between various components into a single place.
  • Open/Closed Principle: You can introduce new mediators without having to change the actual components.
  • You can reduce coupling between various components of a program.
  • You can reuse individual components more easily.

Cons:

  • Over time a mediator can evolve into a God Object.
  • Chain of Responsibility, Command, Mediator, and Observer address various ways of connecting senders and receivers of requests.
  • Mediator and Observer are often competitors. The difference is that Mediator centralizes communication, while Observer distributes it by allowing objects to subscribe to events.
  • Facade and Mediator have similar jobs: they try to facilitate collaboration between lots of tightly coupled classes. However, Facade provides a simplified interface to a subsystem, whereas Mediator centralizes communication between components within that system.

// Mediator
public interface IChatMediator
{
void SendMessage(string message, User user);
}
// Concrete Mediator
public class ChatMediator : IChatMediator
{
private readonly List&lt;User&gt; _users = new();
public void AddUser(User user) => _users.Add(user);
public void SendMessage(string message, User sender)
{
foreach (var user in _users)
{
if (user != sender)
user.Receive(message);
}
}
}
// Colleague
public abstract class User
{
protected IChatMediator _mediator;
protected string _name;
protected User(IChatMediator mediator, string name)
{
_mediator = mediator;
_name = name;
}
public abstract void Send(string message);
public abstract void Receive(string message);
}
// Concrete Colleague
public class ChatUser : User
{
public ChatUser(IChatMediator mediator, string name) : base(mediator, name) {}
public override void Send(string message)
{
Console.WriteLine($"{_name} envia: {message}");
_mediator.SendMessage(message, this);
}
public override void Receive(string message)
{
Console.WriteLine($"{_name} recebe: {message}");
}
}
// Usage
class Program
{
static void Main()
{
var mediator = new ChatMediator();
var user1 = new ChatUser(mediator, "Ana");
var user2 = new ChatUser(mediator, "Bruno");
var user3 = new ChatUser(mediator, "Carla");
mediator.AddUser(user1);
mediator.AddUser(user2);
mediator.AddUser(user3);
user1.Send("Hello everybody!");
}
}