A set of N collaborators each hold direct references to several others, creating a tangled web of inter-object dependencies. Adding one new collaborator means editing existing collaborators' constructors and onChange/onUpdate methods; testing any one in isolation requires stubbing all of its collaborators.
Each collaborator knows one Mediator; the Mediator owns the coordination logic. Inter-collaborator relationships live in one place; collaborators are unit-testable with a stub mediator; new relationships are mediator-internal edits.
Before the pattern
class CountryField {constructor(stateField, postalField) {this.stateField = stateField;this.postalField = postalField;}onChange(value) {this.stateField.setOptions(statesFor(value));this.postalField.setPattern(postalPatternFor(value));}}class StateField {constructor(cityField) {this.cityField = cityField;}onChange(value) {this.cityField.setOptions(citiesFor(value));}}// Each widget knows multiple others; O(N²) potential edges;// testing one widget requires mocking each collaborator;// adding a 'phone' field that depends on country touches every constructor.
After the pattern
class FormMediator {constructor() {this.country = new CountryField(this);this.state = new StateField(this);this.city = new CityField(this);this.postal = new PostalField(this);}fieldChanged(field, value) {if (field === this.country) {this.state.setOptions(statesFor(value));this.postal.setPattern(postalPatternFor(value));} else if (field === this.state) {this.city.setOptions(citiesFor(value));}}}class CountryField {constructor(mediator) {this.mediator = mediator;}onChange(value) {this.mediator.fieldChanged(this, value);}}class StateField {constructor(mediator) {this.mediator = mediator;}onChange(value) {this.mediator.fieldChanged(this, value);}}
Mutual references between widgets / services / aggregates is Insider Trading at its worst — every collaborator depends on every other collaborator's interface. Adding a new collaborator forces Shotgun Surgery across the existing ones; one missed update silently breaks a relationship the test suite may not exercise.
The Mediator becomes a focal point of complexity: all relationship logic lives in one class, which can grow into a god object. The Mediator's onChange method becomes a big switch on which collaborator fired the event; the structural ugliness moves rather than disappearing.
Collaborator constructors take one Mediator argument; testing a collaborator stubs one collaborator (the Mediator); adding a new field is a new file + a new switch arm in the Mediator. Relationships are auditable in one place; refactoring one collaborator does not ripple across others.
Mediators that grow beyond a single coordination concern become Service objects in disguise. When the Mediator has > 5 collaborator types or > 10 relationship rules, split it: distinct Mediator per coordination concern. Otherwise the Mediator itself becomes the Long Function the pattern was supposed to prevent.