Mediator Pattern
Collect interaction rules in a mediator so collaborating objects do not depend directly on each other.
· Last updated: 2026-10-25 · 6 min read
Overview
A submit button is enabled only when details are complete and a confirmation box is checked. If each control directly refers to the others, changing the form reaches several controls.
Mediator coordinates interactions between objects. Each element reports its change, and the mediator decides how other elements should respond.
UML
Toggle reports changes through IFormMediator. FormMediator combines two conditions and sets SubmitButton.Enabled, so Toggle has no direct reference to the button.
History
GoF cataloged Mediator as a behavioral pattern in Design Patterns in 1994. A separate object encapsulates interactions among collaborating objects.1
Why use it?
Controls need not know other controls or the coordination rule. A changed submit condition can be reviewed in the mediator.
Keep the mediator scoped to its collaboration instead of collecting every business operation. This example coordinates one form; it replaces neither server validation nor authorization.
Example
Both details and confirmation must be ready before submission is enabled. Toggle stands in for a UI control that reports changes.
using System;
using System.Collections.Generic;
using System.Linq;
var button = new SubmitButton();
IFormMediator mediator = new FormMediator(button);
new Toggle("details", mediator).Set(true);
Console.WriteLine(button.Enabled);
new Toggle("confirmed", mediator).Set(true);
Console.WriteLine(button.Enabled);
sealed class SubmitButton { public bool Enabled { get; set; } }
interface IFormMediator { bool Changed(string field, bool ready); }
// Compute relationships among widgets in this mediator.
sealed class FormMediator(SubmitButton button) : IFormMediator
{
private bool details;
private bool confirmed;
public bool Changed(string field, bool ready)
{
switch (field)
{
case "details": details = ready; break;
case "confirmed": confirmed = ready; break;
default: return false;
}
button.Enabled = details && confirmed;
return true;
}
}
// The widget reports its change without controlling another widget.
sealed class Toggle(string field, IFormMediator mediator)
{
public bool Set(bool ready) => mediator.Changed(field, ready);
}
False
TrueThe first condition alone leaves the button disabled. The second completes the condition.
An extended example
Turn one condition off again, then submit an unknown field name.
using System;
using System.Collections.Generic;
using System.Linq;
var button = new SubmitButton();
IFormMediator mediator = new FormMediator(button);
mediator.Changed("details", true);
mediator.Changed("confirmed", true);
Console.WriteLine(button.Enabled);
mediator.Changed("details", false);
Console.WriteLine(button.Enabled);
Console.WriteLine(mediator.Changed("unknown", true));
Console.WriteLine(button.Enabled);
sealed class SubmitButton { public bool Enabled { get; set; } }
interface IFormMediator { bool Changed(string field, bool ready); }
// Compute relationships among widgets in this mediator.
sealed class FormMediator(SubmitButton button) : IFormMediator
{
private bool details;
private bool confirmed;
public bool Changed(string field, bool ready)
{
switch (field)
{
case "details": details = ready; break;
case "confirmed": confirmed = ready; break;
default: return false;
}
button.Enabled = details && confirmed;
return true;
}
}
// The widget reports its change without controlling another widget.
sealed class Toggle(string field, IFormMediator mediator)
{
public bool Set(bool ready) => mediator.Changed(field, ready);
}
True
False
False
FalseReleasing a condition disables the button. An unknown field returns false without changing existing state. Observer separates notification relationships; the focus here is the coordination that follows a notification.
TypeScript example
Each Toggle reports a change; FormMediator owns the relationship between the two conditions and the button. A toggle no longer needs to know the other toggle or manipulate the button directly.
class SubmitButton { enabled = false; }
interface IFormMediator { changed(field: string, value: boolean): boolean; }
// Compute relationships among widgets in this mediator.
class FormMediator implements IFormMediator {
private details = false;
private confirmed = false;
constructor(private readonly button: SubmitButton) {}
changed(field: string, value: boolean): boolean {
if (field === "details") this.details = value;
else if (field === "confirmed") this.confirmed = value;
else return false;
this.button.enabled = this.details && this.confirmed;
return true;
}
}
// The widget reports its change without controlling another widget.
class Toggle {
constructor(private readonly field: string, private readonly mediator: IFormMediator) {}
set(value: boolean): boolean { return this.mediator.changed(this.field, value); }
}
const button = new SubmitButton();
const mediator = new FormMediator(button);
new Toggle("details", mediator).set(true);
console.log("after details: " + button.enabled);
new Toggle("confirmed", mediator).set(true);
console.log("after confirmation: " + button.enabled);
console.log("unknown accepted: " + mediator.changed("unknown", false));
console.log("still enabled: " + button.enabled);after details: false
after confirmation: true
unknown accepted: false
still enabled: trueAn unknown field returns false without changing the button. This coordinates a UI; server-side validation and authorization for submission remain separate.
Related reading
Each C# example is a separate .NET 10 console program.
Compile the TypeScript examples for ES2022 or later and run the emitted JavaScript with Node.js.
Footnotes
- Gamma, Helm, Johnson, Vlissides. Design Patterns (1994). Publisher record. Behavioral patterns, Mediator. ↩