Behavioral Patterns «Prev Next»

Lesson 13Mediator: traffic flow course project
ObjectiveIncorporate the Mediator pattern into the traffic flow system.

Implementing a Traffic Flow Mediator in Java

This course project applies the Mediator behavioral design pattern to four directional traffic signals. Instead of controlling one another directly, the North, East, South, and West signals submit green requests through a mediator. The mediator preserves arrival order and decides when the next request may be granted.

This deterministic teaching model is not software for a real roadway controller. It models four independent directions, RED and GREEN aspects, FIFO requests, and a minimum green interval. A production intersection would require conflict groups, clearance and pedestrian phases, sensors, fault detection, overrides, hardware interlocks, safety standards, and extensive verification.


Why the Traffic Signals Need a Mediator

Without a mediator, North might need references to every peer, know which signal is green, track its elapsed time, select the next request, and command each transition. Repeating those decisions across four signals would couple the group, and a new timing rule or signal type could require changes to several classes.

Mediator moves this cross-object policy into one collaboration object. Each TrafficSignal stores its direction and aspect and reports requests through SignalMediator. It never selects or changes a peer. TrafficFlowMediator owns the queue, timing policy, current-green state, and transition order.

The pattern localizes interaction complexity rather than eliminating it. It works best when the mediator remains cohesive around intersection coordination and each signal retains its local state and display behavior. This boundary keeps the collaboration understandable as its rules evolve.

Mediator Pattern Roles in the Traffic System

GoF roleProject participantResponsibility
MediatorSignalMediatorDefines how a signal submits a green request without knowing another signal.
ConcreteMediatorTrafficFlowMediatorRegisters the four signals, queues requests, tracks elapsed time, and grants the next eligible request.
ColleagueTrafficSignalStores its direction and aspect, reports requests to the mediator, and displays state changes authorized by the mediator.
Colleague stateDirection and AspectRepresent the identity and current display state used by the collaboration.

No abstract Colleague superclass is required because TrafficSignal represents every participating signal. Each signal depends only on the narrow mediator interface. The concrete mediator knows the registered signals because it owns their collaboration policy.

Four traffic signals send coordination requests through a central mediator.
Figure 1: The traffic signals communicate through the mediator, which owns request ordering and timing policy.

The connections represent colleague-to-mediator communication, not direct signal commands. A signal requests green without knowing which peer is active, and the mediator authorizes the required aspect changes.

Traffic Coordination Rules

  1. All four signals begin red.
  2. A signal submits a green request to the mediator.
  3. If no direction is green, the mediator grants the first request immediately.
  4. If a direction is already green, later requests enter a first-in-first-out queue.
  5. A duplicate queued request is ignored, so one direction cannot occupy several queue positions.
  6. A request from the direction that is already green is ignored.
  7. The mediator cannot replace the current green signal until its minimum green interval has elapsed.
  8. During a change, the mediator sets the current signal to red before it sets the next requested signal to green.
  9. At most one direction is green in this teaching model.

The minimum interval prohibits an early switch; it does not require rotation when the interval expires. With no waiting request, the current signal may remain green. This demand-driven rule replaces the legacy periodic timer, which changed signals even without demand.

advanceTime provides a deterministic simulated clock, demonstrating ten-second timing without waiting or background threads. A larger application could inject a Clock and scheduler, but scheduling mechanics are separate from Mediator.


Design Patterns Explained

Request and Transition Sequence

  1. At 0 seconds, North requests green. Because no signal is green, the mediator grants North immediately.
  2. At 0 seconds, East requests green. The mediator queues East because North has not completed its ten-second minimum.
  3. At 5 seconds, South requests green. South is placed behind East.
  4. At 10 seconds, the minimum interval for North has elapsed. The mediator changes North to red and grants East.
  5. At 20 seconds, East has completed its minimum interval. The mediator changes East to red and grants South.

The signals initiate meaningful requests, while the mediator determines their effects. No signal inspects or commands a peer.

Java Implementation of the Traffic Flow Mediator

The following example is a complete single-file Java program. Its nested types keep the course example easy to copy and run. In a larger project, each major type could be moved into its own source file without changing the pattern roles or dependency direction.


import java.time.Duration;
import java.util.ArrayDeque;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
public class TrafficFlowMediatorDemo {
    enum Direction {
        NORTH, EAST, SOUTH, WEST
    }
    enum Aspect {
        RED, GREEN
    }
    interface SignalMediator {
        void requestGreen(TrafficSignal signal);
    }
    static final class TrafficSignal {
        private final Direction direction;
        private final SignalMediator mediator;
        private Aspect aspect = Aspect.RED;
        TrafficSignal(Direction direction, SignalMediator mediator) {
            this.direction = direction;
            this.mediator = mediator;
        }
        void requestGreen() {
            mediator.requestGreen(this);
        }
        Direction direction() {
            return direction;
        }
        void show(Aspect nextAspect, long atSeconds) {
            if (aspect != nextAspect) {
                aspect = nextAspect;
                System.out.printf("%2ds: %s signal -> %s%n",
                        atSeconds, direction, aspect);
            }
        }
    }
    static final class TrafficFlowMediator implements SignalMediator {
        private final long minimumGreenSeconds;
        private final Map<Direction, TrafficSignal> signals =
                new EnumMap<>(Direction.class);
        private final Queue<Direction> requests = new ArrayDeque<>();
        private final Set<Direction> queued = new HashSet<>();
        private Direction currentGreen;
        private long greenSince;
        private long simulatedSeconds;
        TrafficFlowMediator(Duration minimumGreenTime) {
            if (minimumGreenTime.isZero() || minimumGreenTime.isNegative()) {
                throw new IllegalArgumentException(
                        "Minimum green time must be positive");
            }
            minimumGreenSeconds = minimumGreenTime.toSeconds();
            for (Direction direction : Direction.values()) {
                signals.put(direction, new TrafficSignal(direction, this));
            }
        }
        TrafficSignal signal(Direction direction) {
            return signals.get(direction);
        }
        @Override
        public void requestGreen(TrafficSignal signal) {
            Direction requested = signal.direction();
            if (requested == currentGreen || !queued.add(requested)) {
                return;
            }
            requests.add(requested);
            System.out.printf("%2ds: queued %s request%n",
                    simulatedSeconds, requested);
            grantNextIfAllowed();
        }
        void advanceTime(Duration elapsed) {
            if (elapsed.isNegative()) {
                throw new IllegalArgumentException(
                        "Elapsed time cannot be negative");
            }
            simulatedSeconds += elapsed.toSeconds();
            grantNextIfAllowed();
        }
        private void grantNextIfAllowed() {
            if (requests.isEmpty()) {
                return;
            }
            if (currentGreen != null
                    && simulatedSeconds - greenSince
                    < minimumGreenSeconds) {
                return;
            }
            if (currentGreen != null) {
                signals.get(currentGreen).show(Aspect.RED, simulatedSeconds);
            }
            Direction next = requests.remove();
            queued.remove(next);
            signals.get(next).show(Aspect.GREEN, simulatedSeconds);
            currentGreen = next;
            greenSince = simulatedSeconds;
        }
    }
    public static void main(String[] args) {
        TrafficFlowMediator mediator =
                new TrafficFlowMediator(Duration.ofSeconds(10));
        TrafficSignal north = mediator.signal(Direction.NORTH);
        TrafficSignal east = mediator.signal(Direction.EAST);
        TrafficSignal south = mediator.signal(Direction.SOUTH);
        north.requestGreen();
        east.requestGreen();
        mediator.advanceTime(Duration.ofSeconds(5));
        south.requestGreen();
        mediator.advanceTime(Duration.ofSeconds(5));
        mediator.advanceTime(Duration.ofSeconds(10));
    }
}

Program Output

 0s: queued NORTH request
 0s: NORTH signal -> GREEN
 0s: queued EAST request
 5s: queued SOUTH request
10s: NORTH signal -> RED
10s: EAST signal -> GREEN
20s: EAST signal -> RED
20s: SOUTH signal -> GREEN

North is granted immediately, while East and South retain arrival order. Nothing changes at five seconds because North's minimum is incomplete. The ten- and twenty-second transitions show that the mediator owns timing and selection.

How the Java Classes Implement Mediator

  1. TrafficSignal.requestGreen() sends a request through the SignalMediator interface. The signal stores no references to its peers.
  2. TrafficFlowMediator.requestGreen() rejects a request from the current green direction, deduplicates queued requests with a Set, appends each new direction to the FIFO Queue, and tries to grant the next request.
  3. grantNextIfAllowed() returns without changing the system when the queue is empty or the current minimum green interval is incomplete.
  4. Once a change is allowed, the mediator sends RED to the current signal before it sends GREEN to the next signal.
  5. The mediator records the new current direction and start time so later requests can be evaluated consistently.
  6. EnumMap makes the direction-to-signal registry explicit. The queue preserves request order, while the set prevents duplicate queue positions.

TrafficFlowMediator is more than a forwarding object. It applies ordering, duplicate suppression, mutual exclusion, timing eligibility, and transition order. Merely relaying a call from North to East would add indirection without meaningful collaboration policy.

A new queue or timing policy would primarily affect the mediator, while each signal remains focused on requests and authorized displays. Changes to the contract or a signal's fundamental capabilities can still affect both sides. Mediator reduces direct peer coupling; it does not isolate every future change.

Design Decisions and Trade-Offs

DecisionBenefitLimitation
FIFO request queueProduces predictable arrival-order serviceDoes not model priorities, emergency preemption, or starvation policy
One green directionMakes mutual exclusion easy to observeDoes not model compatible movement groups
Simulated timeCreates deterministic, fast demonstrations and testsDoes not schedule real asynchronous transitions
Duplicate suppressionPrevents repeated queue entries for one directionDoes not count repeated demand or traffic volume
Centralized coordinationRemoves direct signal-to-signal dependenciesThe mediator can become too large if unrelated responsibilities accumulate

The mediator should coordinate this collaboration without absorbing the entire traffic domain. Hardware communication, fault diagnosis, sensors, persistence, interfaces, and policy calculation can remain separate. Owning all of them would turn TrafficFlowMediator into a god object.

State can represent multi-phase intersection modes, Command can represent auditable control requests, and Observer can publish aspect changes. They may complement Mediator, but none replaces its coordination responsibility.

Extending the Teaching Model

A next version could add conflict groups, yellow and all-red intervals, pedestrian phases, sensor-based fairness, authorized emergency commands, a replaceable clock, event records, fail-safe states, and explicit lifecycle behavior. Tests should cover queue order, duplicates, timing boundaries, invalid durations, red-before-green transitions, and mutual exclusion.

Real-time scheduling requires a threading model. Multiple request threads require the mutable state to be confined to one event loop or protected by a deliberate synchronization policy. Adding synchronized to one method does not prove system-wide thread safety.

A real controller must also handle partial failure. A failed red command must not be followed blindly by a green command. Hardware acknowledgments, independent interlocks, safe fallback states, diagnostics, and regulation are beyond this GoF example. Its successful run validates only the deterministic teaching sequence.

Course Project Exercise

Write or extend a SignalMediator for the course-project intersection. Register all four signals, queue green requests, enforce the minimum green interval, and verify that only the mediator coordinates transitions. A successful Mediator implementation makes interaction policy explicit and testable while keeping each signal independent of its peers.

Signal Mediator Class - Exercise


SEMrush Software 13 SEMrush Banner 13