LLD CASE STUDIES / OBJECT DESIGN BRIEF

Design an elevator system

Design an elevator system for a building with N floors and M elevator cars.

AdvancedPhase 09 / Topic 2 of 10ResponsibilitiesCollaborationsExtensibility
01

Overview

Design an elevator system for a building with N floors and M elevator cars. Requirements: passengers press hall buttons (up/down) on a floor and car buttons (destination floor) inside a car; a controller assigns hall calls to cars; each car moves floor by floor, stops at requested floors, and opens doors; the scheduling policy should be swappable; and the system should handle maintenance mode and capacity.

The core ideas: each car keeps sorted sets of up-stops and down-stops and follows the SCAN/LOOK algorithm (keep moving in the current direction while there are stops ahead, then reverse). A dispatcher (Mediator) chooses the best car for each hall call via a strategy (nearest suitable car, least busy). The car's state machine (IDLE, MOVING_UP, MOVING_DOWN, DOORS_OPEN, MAINTENANCE) prevents illegal actions.

A bus route that adapts

The bus keeps driving in one direction, picking up and dropping off along the way, and only turns around when there are no more stops ahead. A dispatcher decides which bus should pick up a new waiting passenger.

02

When to use it

  • One of the most common LLD interview questions.
  • Practicing State, Strategy, Mediator, and Observer together.
  • Simulation-style problems with a clock (step function).
03

Where it shows up in interviews

Scheduling with swappable policy

Recognize it when: assign requests to one of many workers.

  • Design an elevator system
  • Design a ride dispatch system
State machine + simulation

Recognize it when: entity moves through states over time.

  • Design a traffic light controller
  • Design a vending machine
04

Where it is used in real software

Destination dispatch

Modern buildings (Otis, KONE, Schindler) ask for the destination in the lobby and group passengers going to nearby floors.

SCAN disk scheduling

The elevator algorithm is also used by operating systems to schedule disk head movement.

Safety systems

Real controllers enforce door interlocks, overload sensors, and fire service modes as state rules.

05

Key terms

Hall call
Request from a floor with a direction.
Car call
Destination selected inside the car.
SCAN / LOOK
Serve all requests in one direction, then reverse.
Dispatcher
Assigns hall calls to cars (Mediator + Strategy).
Tick / step
Simulation advancing time by one unit.
06

How it works, step by step

  1. 1
    Clarify

    Floors, cars, capacity, scheduling goal, maintenance, concurrency.

  2. 2
    Entities

    Building, ElevatorCar, Direction, CarState, Request, Dispatcher, SchedulingStrategy.

  3. 3
    Car logic

    upStops and downStops TreeSets; step() moves one floor or opens doors.

  4. 4
    Dispatch logic

    Strategy scores cars by distance and direction compatibility.

  5. 5
    Edge cases

    No available car, requests to current floor, maintenance, invalid floors.

Car at floor 3 moving up, stops {5, 8} up and {2} down
Step 1 / 4
F2
car
F3 (car)
F5
F8

STEP 1Direction UP. Up-stops ahead: 5 and 8. The down-stop at 2 waits.

07

Dispatcher scoring (nearest suitable car)

Hall call: floor 6, direction UP

Step 1 / 4
CarPosition / directionSuitable?Score (lower is better)
AFloor 4, moving UPYes, 6 is ahead in the same direction2
BFloor 7, moving UPPassed floor 6 alreadyPenalty: 1 + 2 x 10
CFloor 1, IDLEYes5
DMAINTENANCENoExcluded

NOWCar: A | Position / direction: Floor 4, moving UP | Suitable?: Yes, 6 is ahead in the same direction | Score (lower is better): 2

Car A is assigned. Swapping the strategy (for example least-loaded) changes only one class.

08

Implementation

import java.util.*; enum Direction { UP, DOWN, IDLE }enum CarState { IDLE, MOVING, DOORS_OPEN, MAINTENANCE } final class ElevatorCar {    private final int id, minFloor, maxFloor;    private int floor;    private Direction direction = Direction.IDLE;    private CarState state = CarState.IDLE;    private final TreeSet<Integer> upStops = new TreeSet<>(), downStops = new TreeSet<>();     ElevatorCar(int id, int minFloor, int maxFloor) { this.id = id; this.minFloor = minFloor; this.maxFloor = maxFloor; this.floor = minFloor; }     int id() { return id; }    int floor() { return floor; }    Direction direction() { return direction; }    boolean available() { return state != CarState.MAINTENANCE; }    int pendingStops() { return upStops.size() + downStops.size(); }     void addStop(int target) {        if (target < minFloor || target > maxFloor) throw new IllegalArgumentException("Invalid floor " + target);        if (!available()) throw new IllegalStateException("Car " + id + " in maintenance");        if (target == floor && state != CarState.MOVING) { state = CarState.DOORS_OPEN; return; }        (target > floor ? upStops : downStops).add(target);        if (direction == Direction.IDLE) direction = target > floor ? Direction.UP : Direction.DOWN;    }     /** Advance the simulation by one tick. */    void step() {        if (state == CarState.MAINTENANCE) return;        if (state == CarState.DOORS_OPEN) { state = pendingStops() == 0 ? CarState.IDLE : CarState.MOVING; }        if (pendingStops() == 0) { direction = Direction.IDLE; state = CarState.IDLE; return; }         if (direction == Direction.UP && upStops.isEmpty()) direction = Direction.DOWN;        if (direction == Direction.DOWN && downStops.isEmpty()) direction = Direction.UP;         state = CarState.MOVING;        floor += direction == Direction.UP ? 1 : -1;        TreeSet<Integer> stops = direction == Direction.UP ? upStops : downStops;        if (stops.remove(floor)) state = CarState.DOORS_OPEN;         // stops behind us in the current direction move to the other set (LOOK)        if (direction == Direction.UP) { downStops.addAll(upStops.headSet(floor)); upStops.removeIf(f -> f < floor); }        else { upStops.addAll(downStops.tailSet(floor, false)); downStops.removeIf(f -> f > floor); }    }     void setMaintenance(boolean on) { state = on ? CarState.MAINTENANCE : CarState.IDLE; if (on) { upStops.clear(); downStops.clear(); } }    @Override public String toString() { return "Car" + id + "@" + floor + " " + direction + " " + state; }} interface DispatchStrategy { Optional<ElevatorCar> choose(List<ElevatorCar> cars, int floor, Direction dir); } final class NearestSuitableCar implements DispatchStrategy {    public Optional<ElevatorCar> choose(List<ElevatorCar> cars, int floor, Direction dir) {        return cars.stream().filter(ElevatorCar::available).min(Comparator.comparingInt(c -> score(c, floor, dir)));    }    private int score(ElevatorCar c, int floor, Direction dir) {        int distance = Math.abs(c.floor() - floor);        boolean onTheWay = c.direction() == Direction.IDLE            || (c.direction() == dir && (dir == Direction.UP ? c.floor() <= floor : c.floor() >= floor));        return onTheWay ? distance : distance + 10 * (c.pendingStops() + 1);    }} final class ElevatorController {                     // mediator between buttons and cars    private final List<ElevatorCar> cars;    private DispatchStrategy strategy;     ElevatorController(List<ElevatorCar> cars, DispatchStrategy strategy) { this.cars = List.copyOf(cars); this.strategy = strategy; }    void setStrategy(DispatchStrategy s) { strategy = s; }     ElevatorCar hallCall(int floor, Direction dir) {        ElevatorCar car = strategy.choose(cars, floor, dir).orElseThrow(() -> new IllegalStateException("No car available"));        car.addStop(floor);        return car;    }    void carCall(int carId, int floor) { cars.get(carId).addStop(floor); }    void tick() { cars.forEach(ElevatorCar::step); }}
09

Complexity and performance

Add stopO(log n)

TreeSet insert.

DispatchO(cars)

Score each car.

StepO(log n)

Per car per tick.

10

Trade-offs

Simple nearest-car vs optimal scheduling

Nearest-car heuristics are easy to explain; optimal scheduling (minimizing total wait) is NP-hard in general, so real systems use heuristics and destination dispatch.

Tick simulation vs event-driven

A tick loop is easy to test deterministically; event-driven timing is closer to reality but harder to reason about.

11

Variants and related techniques

Destination dispatch

Passengers enter destinations in the lobby; cars are grouped by destination.

Zoned elevators

Cars serve floor ranges (low-rise, high-rise).

Priority modes

Fire service, VIP, and freight modes as additional states.

12

Common mistakes

  • One giant if/else in a controller for all states.

    Fix: Separate car state logic from dispatch strategy.

  • Serving requests FIFO.

    Fix: Causes excessive back-and-forth; use SCAN/LOOK ordering.

  • Ignoring requests for the current floor.

    Fix: Open doors immediately when idle at that floor.

13

Interview questions

How does a car decide where to go next?

It keeps up and down stop sets and uses the LOOK algorithm: continue in the current direction while there are stops ahead, stopping at each; when none remain ahead, reverse; when no stops remain, go idle.

How would you make scheduling configurable?

Inject a DispatchStrategy into the controller (nearest suitable car, least loaded, zoning). The controller acts as a mediator between hall buttons and cars and delegates the choice to the strategy.

How do you handle concurrency?

Hall and car calls can arrive from many threads; synchronize addStop per car or funnel requests through a single controller thread with a queue, while the simulation loop steps cars.

14

Practice problems

ProblemDifficultyWhat it trains
Single-car elevator with LOOKMediumStop ordering.
Multi-car system with swappable dispatchHardStrategy + Mediator.