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.
Problem
Section titled βProblemβ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.
Solution
Section titled βSolutionβ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.
Real-World Analogy
Section titled βReal-World Analogyβ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.
Structure
Section titled βStructureβ- 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.
- Mediator Interface declares methods of communication with components, which usually include just a single notification method.
- Concrete Mediator encapsulates relations between various components. Concrete mediators often keep references to all components they manage and sometimes even manage their lifecycle.
- Communication Flow: Components notify the mediator about events. The mediator then decides which other components should handle the event and redirects the call.
Structure (Class Diagram)
Section titled βStructure (Class Diagram)β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
Pseudocode
Section titled βPseudocodeβ# 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")Applicability
Section titled βApplicabilityβ- 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.
How to Implement
Section titled βHow to Implementβ- Identify a group of tightly coupled classes which would benefit from being more independent.
- Declare the mediator interface and describe the desired communication protocol between mediators and components.
- Implement the concrete mediator class. This class will store references to all the components it manages.
- Components should store a reference to the mediator object. They should notify the mediator instead of calling other components directly.
- The client must initialize the mediator and pass it to all components.
Pros and Cons
Section titled βPros and Consβ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.
Relations with other Design Patterns
Section titled βRelations with other Design Patternsβ- 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.
Code Examples
Section titled βCode ExamplesβCode Examples (C#)
Section titled βCode Examples (C#)β// Mediatorpublic interface IChatMediator{ void SendMessage(string message, User user);}
// Concrete Mediatorpublic class ChatMediator : IChatMediator{ private readonly List<User> _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); } }}
// Colleaguepublic 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 Colleaguepublic 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}"); }}
// Usageclass 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!"); }}Code Examples (Python)
Section titled βCode Examples (Python)βfrom __future__ import annotationsfrom abc import ABC, abstractmethod
class Mediator(ABC): @abstractmethod def notify(self, sender: object, event: str) -> None: pass
class ConcreteMediator(Mediator): def __init__(self, component1: Component1, component2: Component2) -> None: self._component1 = component1 self._component1.mediator = self self._component2 = component2 self._component2.mediator = self
def notify(self, sender: object, event: str) -> None: if event == "A": print("Mediator reacts on A and triggers following operations:") self._component2.do_c() elif event == "D": print("Mediator reacts on D and triggers following operations:") self._component1.do_b() self._component2.do_c()
class BaseComponent: def __init__(self, mediator: Mediator = None) -> None: self._mediator = mediator
@property def mediator(self) -> Mediator: return self._mediator
@mediator.setter def mediator(self, mediator: Mediator) -> None: self._mediator = mediator
class Component1(BaseComponent): def do_a(self) -> None: print("Component 1 does A.") self.mediator.notify(self, "A")
def do_b(self) -> None: print("Component 1 does B.")
class Component2(BaseComponent): def do_c(self) -> None: print("Component 2 does C.")
def do_d(self) -> None: print("Component 2 does D.") self.mediator.notify(self, "D")
if __name__ == "__main__": c1 = Component1() c2 = Component2() mediator = ConcreteMediator(c1, c2)
print("Client triggers operation A.") c1.do_a()
print("\nClient triggers operation D.") c2.do_d()Code Examples (C++)
Section titled βCode Examples (C++)β#include <iostream>#include <string>#include <vector>
// Mediatorclass IChatMediator {public: virtual ~IChatMediator() = default; virtual void SendMessage(const std::string& message, class User* sender) = 0;};
// Colleagueclass User {protected: IChatMediator& mediator; std::string name;
public: User(IChatMediator& m, const std::string& n) : mediator(m), name(n) {}
virtual ~User() = default;
virtual void Send(const std::string& message) = 0; virtual void Receive(const std::string& message) = 0;};
// Concrete Mediatorclass ChatMediator : public IChatMediator {private: std::vector<User*> users;
public: void AddUser(User* user) { users.push_back(user); }
void SendMessage(const std::string& message, User* sender) override { for (auto* user : users) { if (user != sender) { user->Receive(message); } } }};
// Concrete Colleagueclass ChatUser : public User {public: ChatUser(IChatMediator& m, const std::string& n) : User(m, n) {}
void Send(const std::string& message) override { std::cout << name << " envia: " << message << std::endl; mediator.SendMessage(message, this); }
void Receive(const std::string& message) override { std::cout << name << " recebe: " << message << std::endl; }};
// Usageint main() { ChatMediator mediator;
ChatUser user1(mediator, "Ana"); ChatUser user2(mediator, "Bruno"); ChatUser user3(mediator, "Carla");
mediator.AddUser(&user1); mediator.AddUser(&user2); mediator.AddUser(&user3);
user1.Send("Hello everybody!");
return 0;}Code Examples (Java)
Section titled βCode Examples (Java)β// Mediatorpublic interface IChatMediator { void sendMessage(String message, User user);}
// Concrete Mediatorimport java.util.ArrayList;import java.util.List;
public class ChatMediator implements IChatMediator {
private final List<User> users = new ArrayList<>();
public void addUser(User user) { users.add(user); }
@Override public void sendMessage(String message, User sender) { for (User user : users) { if (user != sender) { user.receive(message); } } }}
// Colleague (Abstract)public abstract class User {
protected IChatMediator mediator; protected String name;
protected User(IChatMediator mediator, String name) { this.mediator = mediator; this.name = name; }
public abstract void send(String message); public abstract void receive(String message);}
// Concrete Colleaguepublic class ChatUser extends User {
public ChatUser(IChatMediator mediator, String name) { super(mediator, name); }
@Override public void send(String message) { System.out.println(name + " envia: " + message); mediator.sendMessage(message, this); }
@Override public void receive(String message) { System.out.println(name + " recebe: " + message); }}
// Usagepublic class Program { public static void main(String[] args) { ChatMediator mediator = new ChatMediator();
User user1 = new ChatUser(mediator, "Ana"); User user2 = new ChatUser(mediator, "Bruno"); User user3 = new ChatUser(mediator, "Carla");
mediator.addUser(user1); mediator.addUser(user2); mediator.addUser(user3);
user1.send("Hello everybody!"); }}Code Examples (JavaScript)
Section titled βCode Examples (JavaScript)β// Mediator (simulated interface)class IChatMediator { sendMessage(message, user) { throw new Error("sendMessage() must be implemented"); }}
// Concrete Mediatorclass ChatMediator extends IChatMediator { constructor() { super(); this.users = []; }
addUser(user) { this.users.push(user); }
sendMessage(message, sender) { for (const user of this.users) { if (user !== sender) { user.receive(message); } } }}
// Colleague (abstract)class User { constructor(mediator, name) { if (new.target === User) { throw new Error("User is abstract and cannot be instantiated directly"); } this.mediator = mediator; this.name = name; }
send(message) { throw new Error("send() must be implemented"); }
receive(message) { throw new Error("receive() must be implemented"); }}
// Concrete Colleagueclass ChatUser extends User { constructor(mediator, name) { super(mediator, name); }
send(message) { console.log(`${this.name} envia: ${message}`); this.mediator.sendMessage(message, this); }
receive(message) { console.log(`${this.name} recebe: ${message}`); }}
// Usageconst mediator = new ChatMediator();
const user1 = new ChatUser(mediator, "Ana");const user2 = new ChatUser(mediator, "Bruno");const user3 = new ChatUser(mediator, "Carla");
mediator.addUser(user1);mediator.addUser(user2);mediator.addUser(user3);
user1.send("Hello everybody!");Code Examples (Visual Basic)
Section titled βCode Examples (Visual Basic)β' ============================' Mediator' ============================Public Interface IChatMediator Sub SendMessage(message As String, user As User)End Interface
' ============================' Concrete Mediator' ============================Public Class ChatMediator Implements IChatMediator
Private ReadOnly _users As New List(Of User)()
Public Sub AddUser(user As User) _users.Add(user) End Sub
Public Sub SendMessage(message As String, sender As User) Implements IChatMediator.SendMessage For Each user In _users If user IsNot sender Then user.Receive(message) End If Next End SubEnd Class
' ============================' Colleague' ============================Public MustInherit Class User Protected ReadOnly _mediator As IChatMediator Protected ReadOnly _name As String
Protected Sub New(mediator As IChatMediator, name As String) _mediator = mediator _name = name End Sub
Public MustOverride Sub Send(message As String) Public MustOverride Sub Receive(message As String)End Class
' ============================' Concrete Colleague' ============================Public Class ChatUser Inherits User
Public Sub New(mediator As IChatMediator, name As String) MyBase.New(mediator, name) End Sub
Public Overrides Sub Send(message As String) Console.WriteLine($"{_name} envia: {message}") _mediator.SendMessage(message, Me) End Sub
Public Overrides Sub Receive(message As String) Console.WriteLine($"{_name} recebe: {message}") End SubEnd Class
' ============================' Usage' ============================Module Program Sub Main() Dim mediator As New ChatMediator()
Dim user1 As New ChatUser(mediator, "Ana") Dim user2 As New ChatUser(mediator, "Bruno") Dim user3 As New ChatUser(mediator, "Carla")
mediator.AddUser(user1) mediator.AddUser(user2) mediator.AddUser(user3)
user1.Send("Hello everybody!") End SubEnd ModuleCode Examples (Rust)
Section titled βCode Examples (Rust)βuse std::cell::RefCell;use std::rc::Rc;
// Mediatortrait ChatMediator { fn send_message(&self, message: &str, sender: Rc<RefCell<dyn User>>); fn add_user(&mut self, user: Rc<RefCell<dyn User>>);}
// Concrete Mediatorstruct ConcreteChatMediator { users: Vec<Rc<RefCell<dyn User>>>,}
impl ConcreteChatMediator { fn new() -> Self { Self { users: Vec::new() } }}
impl ChatMediator for ConcreteChatMediator { fn add_user(&mut self, user: Rc<RefCell<dyn User>>) { self.users.push(user); }
fn send_message(&self, message: &str, sender: Rc<RefCell<dyn User>>) { for user in &self.users { if !Rc::ptr_eq(user, &sender) { user.borrow().receive(message); } } }}
// Colleaguetrait User { fn send(&self, message: &str); fn receive(&self, message: &str); fn name(&self) -> &str;}
// Concrete Colleaguestruct ChatUser { mediator: Rc<RefCell<dyn ChatMediator>>, name: String,}
impl ChatUser { fn new(mediator: Rc<RefCell<dyn ChatMediator>>, name: &str) -> Self { Self { mediator, name: name.to_string(), } }}
impl User for ChatUser { fn send(&self, message: &str) { println!("{} envia: {}", self.name, message); self.mediator .borrow() .send_message(message, Rc::new(RefCell::new(ChatUser { mediator: Rc::clone(&self.mediator), name: self.name.clone(), }))); }
fn receive(&self, message: &str) { println!("{} recebe: {}", self.name, message); }
fn name(&self) -> &str { &self.name }}
// Usagefn main() { let mediator = Rc::new(RefCell::new(ConcreteChatMediator::new()));
let user1 = Rc::new(RefCell::new(ChatUser::new(Rc::clone(&mediator), "Ana"))); let user2 = Rc::new(RefCell::new(ChatUser::new(Rc::clone(&mediator), "Bruno"))); let user3 = Rc::new(RefCell::new(ChatUser::new(Rc::clone(&mediator), "Carla")));
mediator.borrow_mut().add_user(Rc::clone(&user1)); mediator.borrow_mut().add_user(Rc::clone(&user2)); mediator.borrow_mut().add_user(Rc::clone(&user3));
user1.borrow().send("Hello everybody!");}Code Examples (PHP)
Section titled βCode Examples (PHP)β<?php
// Mediatorinterface IChatMediator{ public function sendMessage(string $message, User $user): void;}
// Concrete Mediatorclass ChatMediator implements IChatMediator{ /** @var User[] */ private array $users = [];
public function addUser(User $user): void { $this->users[] = $user; }
public function sendMessage(string $message, User $sender): void { foreach ($this->users as $user) { if ($user !== $sender) { $user->receive($message); } } }}
// Colleagueabstract class User{ protected IChatMediator $mediator; protected string $name;
public function __construct(IChatMediator $mediator, string $name) { $this->mediator = $mediator; $this->name = $name; }
abstract public function send(string $message): void; abstract public function receive(string $message): void;}
// Concrete Colleagueclass ChatUser extends User{ public function send(string $message): void { echo "{$this->name} envia: {$message}\n"; $this->mediator->sendMessage($message, $this); }
public function receive(string $message): void { echo "{$this->name} recebe: {$message}\n"; }}
// Usage$mediator = new ChatMediator();
$user1 = new ChatUser($mediator, "Ana");$user2 = new ChatUser($mediator, "Bruno");$user3 = new ChatUser($mediator, "Carla");
$mediator->addUser($user1);$mediator->addUser($user2);$mediator->addUser($user3);
$user1->send("Hello everybody!");Code Examples (Go)
Section titled βCode Examples (Go)βpackage main
import ( "fmt")
// Mediatortype ChatMediator interface { SendMessage(message string, sender User) AddUser(user User)}
// Concrete Mediatortype ConcreteChatMediator struct { users []User}
func NewChatMediator() *ConcreteChatMediator { return &ConcreteChatMediator{ users: []User{}, }}
func (m *ConcreteChatMediator) AddUser(user User) { m.users = append(m.users, user)}
func (m *ConcreteChatMediator) SendMessage(message string, sender User) { for _, u := range m.users { if u != sender { u.Receive(message) } }}
// Colleaguetype User interface { Send(message string) Receive(message string) Name() string}
// Concrete Colleaguetype ChatUser struct { mediator ChatMediator name string}
func NewChatUser(mediator ChatMediator, name string) *ChatUser { return &ChatUser{ mediator: mediator, name: name, }}
func (u *ChatUser) Name() string { return u.name}
func (u *ChatUser) Send(message string) { fmt.Printf("%s envia: %s\n", u.name, message) u.mediator.SendMessage(message, u)}
func (u *ChatUser) Receive(message string) { fmt.Printf("%s recebe: %s\n", u.name, message)}
// Usagefunc main() { mediator := NewChatMediator()
user1 := NewChatUser(mediator, "Ana") user2 := NewChatUser(mediator, "Bruno") user3 := NewChatUser(mediator, "Carla")
mediator.AddUser(user1) mediator.AddUser(user2) mediator.AddUser(user3)
user1.Send("Hello everybody!")}Code Examples (Ruby)
Section titled βCode Examples (Ruby)β# Mediatorclass ChatMediatordef initialize @users = []end
def add_user(user) @users << userend
def send_message(message, sender) @users.each do |user| user.receive(message) unless user == sender endendend
# Colleagueclass Userdef initialize(mediator, name) @mediator = mediator @name = nameend
def send(message) raise NotImplementedError, "Subclasses must implement send"end
def receive(message) raise NotImplementedError, "Subclasses must implement receive"endend
# Concrete Colleagueclass ChatUser < Userdef send(message) puts "#{@name} envia: #{message}" @mediator.send_message(message, self)end
def receive(message) puts "#{@name} recebe: #{message}"endend
# Usagemediator = ChatMediator.new
user1 = ChatUser.new(mediator, "Ana")user2 = ChatUser.new(mediator, "Bruno")user3 = ChatUser.new(mediator, "Carla")
mediator.add_user(user1)mediator.add_user(user2)mediator.add_user(user3)
user1.send("Hello everybody!")Code Examples (Perl)
Section titled βCode Examples (Perl)β#!/usr/bin/perluse strict;use warnings;
############################# Mediator (Interface-like)############################package ChatMediator;
sub send_message { die "send_message() must be implemented by subclass";}
############################# Concrete Mediator############################package ConcreteChatMediator;our @ISA = ('ChatMediator');
sub new { my ($class) = @_; return bless { users => [] }, $class;}
sub add_user { my ($self, $user) = @_; push @{ $self->{users} }, $user;}
sub send_message { my ($self, $message, $sender) = @_;
foreach my $user (@{ $self->{users} }) { next if $user eq $sender; $user->receive($message); }}
############################# Colleague (Abstract)############################package User;
sub new { my ($class, $mediator, $name) = @_; return bless { mediator => $mediator, name => $name }, $class;}
sub send { die "send() must be implemented by subclass";}
sub receive { die "receive() must be implemented by subclass";}
############################# Concrete Colleague############################package ChatUser;our @ISA = ('User');
sub send { my ($self, $message) = @_; print "$self->{name} envia: $message\n"; $self->{mediator}->send_message($message, $self);}
sub receive { my ($self, $message) = @_; print "$self->{name} recebe: $message\n";}
############################# Usage############################package main;
my $mediator = ConcreteChatMediator->new();
my $user1 = ChatUser->new($mediator, "Ana");my $user2 = ChatUser->new($mediator, "Bruno");my $user3 = ChatUser->new($mediator, "Carla");
$mediator->add_user($user1);$mediator->add_user($user2);$mediator->add_user($user3);
$user1->send("Hello everybody!");Code Examples (TypeScript)
Section titled βCode Examples (TypeScript)β// Mediatorinterface IChatMediator { sendMessage(message: string, user: User): void;}
// Concrete Mediatorclass ChatMediator implements IChatMediator { private users: User[] = [];
addUser(user: User): void { this.users.push(user); }
sendMessage(message: string, sender: User): void { for (const user of this.users) { if (user !== sender) { user.receive(message); } } }}
// Colleague (abstract)abstract class User { protected mediator: IChatMediator; protected name: string;
constructor(mediator: IChatMediator, name: string) { this.mediator = mediator; this.name = name; }
abstract send(message: string): void; abstract receive(message: string): void;}
// Concrete Colleagueclass ChatUser extends User { constructor(mediator: IChatMediator, name: string) { super(mediator, name); }
send(message: string): void { console.log(`${this.name} envia: ${message}`); this.mediator.sendMessage(message, this); }
receive(message: string): void { console.log(`${this.name} recebe: ${message}`); }}
// Usageconst mediator = new ChatMediator();
const user1 = new ChatUser(mediator, "Ana");const user2 = new ChatUser(mediator, "Bruno");const user3 = new ChatUser(mediator, "Carla");
mediator.addUser(user1);mediator.addUser(user2);mediator.addUser(user3);
user1.send("Hello everybody!");