I. Mediator — Behavioral Pattern
Mediator centralizes how a set of objects communicate. Instead of each object holding direct references to every other object it needs to talk to, they all hold a single reference to a mediator, and route every interaction through it. The mediator alone knows how the pieces fit together; the objects it coordinates — the colleagues — only know about the mediator.
This solves the problem of tangled many-to-many communication. Without Mediator, adding a new colleague usually means wiring it up to every other colleague it needs to interact with, and that web of direct references gets harder to follow and change as the object count grows. Mediator flattens that web into a star shape: every colleague talks to one hub, and the hub decides what happens next.
It's the same shape as an air traffic control tower coordinating planes that never talk to each other directly, or a chat room server relaying messages between clients that have no direct connection.
II. Real-world Example
A coffee shop where the cashier and the barista never call each other directly — they both go through a mediator that knows the current workflow:
- Mediator (interface) — declares
Notify(sender, event) string, the single entry point every colleague calls into.
- CoffeeShopMediator — the concrete hub. It holds references to the
Cashierand theBarista, and itsNotifymethod decides what to do based on the event: an"order-received"event routes to the barista, a"drink-ready"event routes to the cashier.
- Cashier (colleague) — exposes
TakeOrder(drinkName), which notifies the mediator with"order-received", andCallCustomer(drinkName), which the mediator invokes when a drink is ready.
- Barista (colleague) — exposes
FinishDrink(drinkName), which notifies the mediator with"drink-ready", andPrepareDrink(drinkName), which the mediator invokes when an order comes in.
Neither Cashier nor Barista holds a reference to the other — they only know about Mediator.

III. Implementation
mediator.go
package mediator
type Mediator interface {
Notify(sender string, event string) string
}coffee_shop_mediator.go
package mediator
type CoffeeShopMediator struct {
cashier *Cashier
barista *Barista
}
func NewCoffeeShopMediator() *CoffeeShopMediator {
return &CoffeeShopMediator{}
}
func (m *CoffeeShopMediator) SetCashier(cashier *Cashier) {
m.cashier = cashier
}
func (m *CoffeeShopMediator) SetBarista(barista *Barista) {
m.barista = barista
}
func (m *CoffeeShopMediator) Notify(sender string, event string) string {
switch event {
case "order-received":
return m.barista.PrepareDrink(sender)
case "drink-ready":
return m.cashier.CallCustomer(sender)
default:
return "Unknown event"
}
}colleagues.go
package mediator
type Cashier struct {
Name string
mediator Mediator
}
func NewCashier(name string, mediator Mediator) *Cashier {
return &Cashier{Name: name, mediator: mediator}
}
func (c *Cashier) TakeOrder(drinkName string) string {
return c.mediator.Notify(drinkName, "order-received")
}
func (c *Cashier) CallCustomer(drinkName string) string {
return c.Name + " called customer for " + drinkName
}
type Barista struct {
Name string
mediator Mediator
}
func NewBarista(name string, mediator Mediator) *Barista {
return &Barista{Name: name, mediator: mediator}
}
func (b *Barista) PrepareDrink(drinkName string) string {
return b.Name + " prepared " + drinkName
}
func (b *Barista) FinishDrink(drinkName string) string {
return b.mediator.Notify(drinkName, "drink-ready")
}main.go (usage)
coffeeShopMediator := mediator.NewCoffeeShopMediator()
cashier := mediator.NewCashier("Mia", coffeeShopMediator)
barista := mediator.NewBarista("Leo", coffeeShopMediator)
coffeeShopMediator.SetCashier(cashier)
coffeeShopMediator.SetBarista(barista)
fmt.Println(cashier.TakeOrder("Americano"))
fmt.Println(barista.FinishDrink("Americano"))IV. Explain the example above
mediator.NewCoffeeShopMediator()creates an empty hub with no colleagues yet.
mediator.NewCashier("Mia", coffeeShopMediator)andmediator.NewBarista("Leo", coffeeShopMediator)create the two colleagues, each holding a reference to the same mediator — never to each other.
coffeeShopMediator.SetCashier(cashier)andSetBarista(barista)complete the wiring, giving the mediator references back to both colleagues.
cashier.TakeOrder("Americano")callsmediator.Notify("Americano", "order-received"). The mediator's switch matches"order-received"and callsbarista.PrepareDrink("Americano"), returning"Leo prepared Americano".
barista.FinishDrink("Americano")callsmediator.Notify("Americano", "drink-ready"). The mediator matches"drink-ready"and callscashier.CallCustomer("Americano"), returning"Mia called customer for Americano".
Running it produces:
*** Example Mediator ***
Leo prepared Americano
Mia called customer for Americano
*** End of Mediator ***At no point does Cashier call Barista or vice versa. Both only ever call mediator.Notify, and CoffeeShopMediator is the only piece of code that knows an order flows from cashier to barista and a finished drink flows from barista back to cashier.
V. Conclusion
Mediator earns its keep once a growing set of objects needs to coordinate with each other and the direct references between them start multiplying — three colleagues means up to six pairwise relationships, and that grows quadratically. Routing everything through one hub keeps that complexity in a single, inspectable place, and adding a new colleague means changing the mediator, not every existing colleague.
The trade-off is that the mediator itself can turn into a god object: all the coordination logic piles into one switch statement, and that switch grows with every new event type. For two objects that only ever talk to each other, a direct method call is simpler than introducing a hub. Mediator pays off once there are three or more colleagues whose interactions would otherwise be wired directly into each other.
No pattern is universally superior.
VI. References
- Go Design Patterns (Mario Castro Contreras)




