We add a tower. Flights report to it and wait for instructions. Only the tower knows who is on the runway and who is waiting.
The mediator
interface ControlTower {
void requestLanding(Flight flight);
void runwayCleared(Flight flight);
}A flight can report two events. That is the whole contract (the list of methods every tower must have).
The concrete mediator
class BengaluruTower implements ControlTower {
private Flight onRunway;
private final Queue<Flight> holding = new ArrayDeque<>();
public void requestLanding(Flight flight) {
if (onRunway == null) {
onRunway = flight;
flight.receive("cleared to land on runway 09L");
} else {
holding.add(flight);
flight.receive("hold, you are number " + holding.size() + " in the queue");
}
}
public void runwayCleared(Flight flight) {
if (flight != onRunway) {
return;
}
onRunway = holding.poll();
if (onRunway != null) {
onRunway.receive("runway free, cleared to land on runway 09L");
}
}
}The runway rule and the queue live here and nowhere else. If the runway is free, the flight lands. If not, it joins the queue (holding). When the runway clears, holding.poll() takes the flight that has waited longest, and the tower tells it to land. First come, first served. Nobody polls.
The check flight != onRunway ignores a clear message from a flight that is not on the runway.
The colleague
class Flight {
private final String code;
private final ControlTower tower;
void requestLanding() {
tower.requestLanding(this);
}
void vacateRunway() {
tower.runwayCleared(this);
}
void receive(String instruction) { ... }
}No list of other flights. No loops. One field points at the tower, and its type is the interface, not BengaluruTower.
Using it
ControlTower tower = new BengaluruTower();
Flight indigo = new Flight("6E-512", tower);
Flight airIndia = new Flight("AI-803", tower);
Flight akasa = new Flight("QP-1104", tower);There is no addPeer loop. A SpiceJet flight that shows up later is just new Flight("SG-160", tower).
Before vs after
| Before | After | |
|---|---|---|
| Flight-held references for 10 flights | 90 (10 x 9) | 10, one per flight, to the tower |
| Where the runway rule lives | Inside every Flight | BengaluruTower only |
| Who decides who lands next | Nobody, flights keep retrying | The tower, from a FIFO queue |
| Adding a flight | Wire it to all existing flights | Pass the tower to its constructor |
| Second runway | Change Flight | Change or replace the tower |
| Testing a flight | Needs real peer flights | Needs a fake ControlTower |
What the run prints
6E-512: requesting landing
Tower to 6E-512: cleared to land on runway 09L
AI-803: requesting landing
Tower to AI-803: hold, you are number 1 in the queue
QP-1104: requesting landing
Tower to QP-1104: hold, you are number 2 in the queue
6E-512: vacated the runway
Tower to AI-803: runway free, cleared to land on runway 09L
AI-803: vacated the runway
Tower to QP-1104: runway free, cleared to land on runway 09L
SG-160: requesting landing
Tower to SG-160: hold, you are number 1 in the queue
QP-1104: vacated the runway
Tower to SG-160: runway free, cleared to land on runway 09L
AI-803 lands because the tower told it to, not because Main asked a second time.
Things to keep in mind
- The tower coordinates. It does not fly. Fuel checks, landing gear and so on stay in
Flight. If the tower starts doing that, it is turning into a God class (one class that does everything). - This tower is not thread safe, so two requests at the same moment could break it. With real concurrent requests you would make
requestLandingandrunwayClearedsynchronized, or run the tower on a single thread. At least that fix is now in one class. - If the airport gets more complex (two runways, priority for low fuel), you change the tower and the flights do not notice.
Reference counting and edge cases
“10 references instead of 90” counts only the references stored by flights: one tower reference each, instead of nine peer references. The tower also keeps references to the current and queued flights, so it can call them back. Mediator removes the links between peers, not every reference.
This small sample assumes each flight requests once and only the current flight clears the runway. A repeated request could add the same Flight to the queue twice. A runwayCleared call from the wrong flight is silently ignored. Production code should model explicit flight states, reject duplicate queue entries, log invalid transitions, and make state changes atomic (all or nothing).
Run this version
From the repository root:
mkdir -p out
javac -d out src/DesignPatterns/BehaviouralPatterns/MediatorPattern/After/Main.java
java -cp out DesignPatterns.BehaviouralPatterns.MediatorPattern.After.MainRead the output as a handoff. A flight reports an event, the tower updates the shared state, and the tower calls the next flight. If BengaluruTower starts calculating fuel, weather, fares, and passenger rules too, it is no longer a coordinator. It has become a God object. Move those decisions into small, separate services or policies, and let the mediator only call them in order.