As objects multiply, code can turn into spaghetti. Once a screen’s view controller, networking, cache, and logger start calling one another directly, fixing one place breaks something somewhere else.
That is where Mediator, Observer, and Facade come in. All three are described as ways to “organize object communication,” but they solve different problems.
Mediator centralizes tangled relationships, Observer delivers one-to-many state-change notifications, and Facade organizes complex internals behind a simple entry point.
Remember this one line and you are halfway there. Below, we will cover when to use each pattern, how they differ, and the code.
The three patterns in one-line summaries
Here are the essentials for when you need a refresher.
- Mediator — Multiple objects communicate through one mediator instead of referencing one another directly
- Observer — When one object’s state changes, all subscribed objects are notified automatically
- Facade — A single simple interface hides a complex subsystem
Even when they all “organize communication,” their roles differ.
Mediator reduces relationship complexity, Observer handles change propagation, and Facade lowers usage complexity.
Mediator vs. Observer: what is the difference?
These two are the easiest to confuse because both stop objects from calling one another directly.
The difference is the direction of the relationship.
Observer has a clear direction. When one Subject changes, the update flows one way to its subscribers. For example, when a new chat message arrives, the screen, notification badge, and sound each react independently.
Mediator has tangled directions. Pressing a button enables a text field, which changes the Save button’s state, and so on. The mediator coordinates this many-to-many relationship on their behalf.
// Observer: state changes propagate in one direction
protocol Observer: AnyObject { func update(_ count: Int) }
final class Cart {
private var observers: [Observer] = []
var items = 0 { didSet { observers.forEach { $0.update(items) } } }
func subscribe(_ o: Observer) { observers.append(o) }
}
final class Badge: Observer {
func update(_ count: Int) { print("Cart badge: \(count)") }
}
let cart = Cart()
cart.subscribe(Badge())
cart.items = 3
// Output: cart badge: 3
The badge follows automatically when the cart changes. Cart does not need to know what Badge is.
With Mediator, the mediator owns the rules between objects.
// Mediator: the mediator manages tangled rules
final class FormMediator {
var isAgreed = false
func agreedChanged(_ value: Bool, submit: Button) {
isAgreed = value
submit.isEnabled = value // Rule: enable submission only after consent
}
}
final class Button { var isEnabled = false }
let submit = Button()
let mediator = FormMediator()
mediator.agreedChanged(true, submit: submit)
print(submit.isEnabled)
// Output: true
The checkbox and button do not need to know each other directly; the mediator enforces the rule “enable submission when consent is given.”
Facade has a somewhat different role
Facade differs fundamentally from the previous two. It does not coordinate communication; it is a pattern that wraps complex internals out of sight.
Suppose processing one order requires calling the payment, inventory, shipping, and notification modules in sequence. The calling code becomes messy.
Facade bundles all of that behind one entry point.
final class OrderFacade {
func placeOrder(_ id: String) {
Payment().charge(id)
Stock().reduce(id)
Delivery().book(id)
print("Order complete: \(id)")
}
}
OrderFacade().placeOrder("A-1024")
// Output: order complete: A-1024
From the outside, calling placeOrder is enough. The caller does not need to know how many modules are inside.
The key point is this: Facade does not make objects communicate; it simplifies the boundary between the caller and the complex system. The direction points inward.
At-a-glance comparison
Here is the way I classify them in practice as of 2026.
| Category | Mediator | Observer | Facade |
|---|---|---|---|
| Problem solved | Tangled many-to-many relationships | State-change propagation | Hiding complex internals |
| Relationship direction | Intertwined (central coordination) | One-to-many (one way) | Caller → system |
| Coupling change | Lower coupling between objects | Subject–subscriber separation | Lower usage complexity |
| Typical example | Form validation, chat-room coordination | Notifications, data binding | Payment and order integration API |
The table makes it immediately clear that the three do not overlap.
When to use them—and when to avoid them
Patterns can become harmful when overused. Put every tangled relationship into a Mediator and it can easily become a God object.
- Mediator — Use when at least 3–4 objects reference one another directly and their rules are tangled. Avoid it when the rules are simple, or the mediator may grow too large.
- Observer — Use when several places need to know about a change in one place. Remember to unsubscribe, or you may cause a memory leak.
- Facade — Use when you want to hide a complex calling procedure. It gets in the way where fine-grained internal control is essential.
In one sentence: use Mediator for tangled relationships, Observer to announce changes, and Facade to hide procedures.
How it comes up in interviews
Q. What is the difference between Mediator and Observer?
Observer is a one-to-many structure in which a Subject’s state changes propagate in one direction to subscribers. Mediator is a many-to-many structure in which a mediator centrally coordinates interaction rules among tangled objects. Direction and relationship complexity are the key distinctions.
Q. Facade also reduces coupling. How does it differ from Mediator?
Facade places a simple interface in front of a subsystem to reduce usage complexity between the caller and the system. Mediator, by contrast, coordinates communication among peer objects. A good answer is: Facade simplifies in one direction; Mediator coordinates interactions.
Rather than memorizing the three patterns, first ask whether your code has a “tangled relationships problem,” a “change propagation problem,” or a “complex procedure problem.” Once you identify the answer, the pattern follows naturally. Pick just one to apply in today’s refactoring.

