Traffic Control
Design the controller behind a single road intersection: a set of traffic lights that cycle through colors, driven by either a fixed timer or a sensor that notices nobody is waiting. The prompt looks like a scheduling problem, but the useful decision is where the "what comes next" logic lives.
Requirements
Functional
- Each
TrafficLightat the intersection is always in exactly one ofRED,YELLOW,GREEN. - The intersection cycles lights so that never more than one direction is
GREENat once. - A light can advance on a fixed timer (
GREENfor 30s,YELLOWfor 5s, ...) or be interrupted by a sensor signaling that its lane is empty and another lane has traffic waiting. - An operator can force the intersection into an all-
REDstate for maintenance without restarting the controller.
Non-functional
- Adding a new light color, or a whole new transition rule (a pedestrian-crossing phase,
say), should not mean rewriting the switch statement inside
IntersectionController. - Every transition must go through validation - a light can never jump straight from
REDtoYELLOW, whatever triggered the change.
Design
The color itself becomes an object instead of a field: a LightState interface with one
method, next(), that returns the state to transition into. TrafficLight just holds
"whichever state object is current" and asks it what comes next - it never contains an
if (color == RED) anywhere. This is the same shape as OrderStatus from
Objects, classes, interfaces - a closed set of
named values - except here each value also carries behavior (its own transition rule), so
it earns the extra machinery of the State pattern
rather than staying a plain enum.
- 1A fixed timer or a sensor event is the only thing that ever asks for a change - the controller does not poll.
- 2The controller picks which light’s turn it is and tells it to move on. It never picks the color itself.
- 3The light asks its current state what comes after it - RED does not know about YELLOW, it just returns it.
- 4The light swaps in the new state object; that swap is the entire transition.
- 5Anyone downstream (a display, a log) reads the color through this, never through the state object.
IntersectionController coordinates multiple TrafficLights and owns the timing/sensor
input; it never touches a light's internal state directly, only calls advance().
Class diagram
Code
Design decisions
- Colors are
LightStateobjects, not an enum with a switch elsewhere. An enum would work for storage, but the transition rule (RED->GREEN->YELLOW->RED) would have to live somewhere, and that somewhere is always a big conditional that grows every time a phase is added. Giving each color its ownnext()keeps every transition rule next to the state it belongs to. - A sensor interrupt is just an early call to
advance(), not a separate code path.TrafficLightdoesn't know or care whetheradvance()was called because a timer fired or because a sensor reported an empty lane - both are "something decided it's time to move on." That's what let the sensor requirement bolt on without touchingLightState. IntersectionControllernever mutates a light's color field. It holds a list ofTrafficLights and callsadvance()on the one whose turn it is; the light is the only class allowed to reassign its own state. This keeps the "never two greens at once" invariant enforceable in one place - the controller's turn-taking logic - instead of scattered across every light.- What's missing for a real system: a maintenance all-red mode needs a state that
refuses to advance until explicitly released (a
MaintenanceStatethat overridesnext()to return itself), and coordinating a full intersection safely needs a transition guard so a light can't goGREENuntil its cross-traffic sibling has confirmedRED- both left out here to keep the example to one clean State-pattern demo rather than a full safety-interlock system.