Skip to content

Elevator System#

Problem statement (interviewer prompt)

Design the control system for a bank of N elevators in a tall building. Model floor calls (up/down), cabin requests, the dispatch algorithm (e.g. nearest-car or zoning), and the elevator's internal state machine. Optimise for waiting time + travel time.

classDiagram
  class ElevatorSystem {
    +cars : List~Car~
    +request(from, to)
    +callFloor(floor, dir)
  }
  class Car {
    +id
    +floor
    +dir : Direction
    +state : State
    +queue
    +move()
  }
  class Dispatcher {
    +assign(req) Car
  }
  ElevatorSystem --> Dispatcher
  ElevatorSystem "1" *-- "many" Car
classDiagram
  direction TB

  class ElevatorSystem {
    -cars: List~Car~
    -dispatcher: Dispatcher
    -listeners: List~Observer~
    +callFloor(floor, dir)
    +request(carId, dest)
  }

  class Car {
    -id: int
    -floor: int
    -dir: Direction
    -state: CarState
    -queue: Stops
    -capacity: int
    -load: int
    +addStop(floor)
    +tick()
  }

  class CarState {
    <<enumeration>>
    IDLE
    MOVING_UP
    MOVING_DOWN
    DOORS_OPEN
    OUT_OF_SERVICE
  }

  class Direction {
    <<enumeration>>
    UP
    DOWN
    IDLE
  }

  class Request {
    -from: int
    -to: int
    -dir: Direction
    -priority: int
    -ts
  }

  class Dispatcher {
    <<interface>>
    +assign(req, cars) Car
  }
  class NearestCar
  class ScanLook
  class GroupControl

  class Door
  class Sensor
  class WeightSensor
  class EmergencyButton
  class FirefighterMode

  ElevatorSystem "1" *-- "many" Car
  ElevatorSystem --> Dispatcher
  Dispatcher <|.. NearestCar
  Dispatcher <|.. ScanLook
  Dispatcher <|.. GroupControl
  Car --> CarState
  Car --> Direction
  Car *-- Door
  Car *-- Sensor
  Car *-- WeightSensor
  Car *-- EmergencyButton
  Car *-- FirefighterMode
  ElevatorSystem --> Request

Scheduling#

  • Nearest Car: simplest; greedy.
  • SCAN / LOOK (elevator algorithm): serve all in current direction before switching.
  • Group control: optimize across cars (most production buildings).

Detailed class model#

The full LLD splits the model into three concerns: the system facade (ElevatorSystem), the per-car runtime (Elevator + its sensors + its door + motor state machines), and the dispatch layer (ElevatorController plus a DispatchStrategy interface). The two request types are kept distinct because they originate from different hardware: a CallRequest comes from a hall panel and carries only the floor and the requested direction, while a FloorRequest comes from inside the cabin and carries a concrete destination. Mixing the two is the most common LLD mistake at this problem.

classDiagram
  direction TB

  class ElevatorSystem {
    -elevators: List~Elevator~
    -controller: ElevatorController
    -directory: BuildingDirectory
    +call(floor, dir)
    +select(carId, dest)
  }

  class Elevator {
    -id: int
    -currentFloor: int
    -direction: Direction
    -motor: MotorState
    -door: DoorState
    -stops: PriorityQueue~Request~
    -load: int
    -capacity: int
    +enqueue(req)
    +tick()
  }

  class ElevatorController {
    -strategy: DispatchStrategy
    +dispatchRequest(call) Elevator
    +rebalance()
  }

  class DispatchStrategy {
    <<interface>>
    +pick(call, fleet) Elevator
  }
  class ScanStrategy
  class LookStrategy
  class NearestCarStrategy
  class DestinationDispatchStrategy

  class Request {
    <<abstract>>
    -timestamp: long
    -priority: int
  }
  class CallRequest {
    -floor: int
    -direction: Direction
  }
  class FloorRequest {
    -destination: int
  }

  class Floor {
    -number: int
    -upPressed: boolean
    -downPressed: boolean
  }

  class DoorState {
    <<enumeration>>
    OPEN
    CLOSING
    CLOSED
    OPENING
    OBSTRUCTED
  }
  class MotorState {
    <<enumeration>>
    STOPPED
    ACCELERATING
    CRUISING
    DECELERATING
    FAULTED
  }
  class Direction {
    <<enumeration>>
    UP
    DOWN
    IDLE
  }

  class BuildingDirectory
  class FloorSensor
  class WeightSensor

  ElevatorSystem "1" *-- "many" Elevator
  ElevatorSystem --> ElevatorController
  ElevatorSystem --> BuildingDirectory
  ElevatorController --> DispatchStrategy
  DispatchStrategy <|.. ScanStrategy
  DispatchStrategy <|.. LookStrategy
  DispatchStrategy <|.. NearestCarStrategy
  DispatchStrategy <|.. DestinationDispatchStrategy
  Request <|-- CallRequest
  Request <|-- FloorRequest
  Elevator --> DoorState
  Elevator --> MotorState
  Elevator --> Direction
  Elevator *-- FloorSensor
  Elevator *-- WeightSensor
  Elevator o-- Request
  ElevatorSystem *-- Floor

The Elevator class owns two independent state machines that the interviewer will ask about: the door (OPEN -> CLOSING -> CLOSED -> OPENING) and the motor (STOPPED -> ACCELERATING -> CRUISING -> DECELERATING). Keeping them separate is what lets a safety interlock fire (door obstruction freezes the door machine while the motor stays in STOPPED) without coupling the two transitions.

Dispatch algorithm in Java#

The controller's job is to pick the elevator whose current trajectory minimises the new caller's wait time. A LOOK-style cost function scores each car by how soon it can reach the requested floor without reversing direction, biasing toward idle cars and against cars whose queue is already deep. The Java sketch below shows the scoring loop and the tie-break.

public final class ElevatorController {
    private final DispatchStrategy strategy;
    private final List<Elevator> fleet;

    public Elevator dispatchRequest(CallRequest call) {
        Elevator best = null;
        long bestScore = Long.MAX_VALUE;
        for (Elevator e : fleet) {
            if (e.motor() == MotorState.FAULTED) continue;
            long score = score(e, call);
            if (score < bestScore) {
                bestScore = score;
                best = e;
            }
        }
        if (best != null) best.enqueue(call);
        return best;
    }

    private long score(Elevator e, CallRequest call) {
        int distance = Math.abs(e.currentFloor() - call.floor());
        int queuePenalty = e.stops().size() * 2;

        if (e.direction() == Direction.IDLE) {
            return distance + queuePenalty;
        }
        boolean sameDirection = (e.direction() == call.direction());
        boolean inPath = (call.direction() == Direction.UP)
            ? call.floor() >= e.currentFloor()
            : call.floor() <= e.currentFloor();

        if (sameDirection && inPath) {
            return distance + queuePenalty;          // along the way
        }
        if (sameDirection && !inPath) {
            return distance + queuePenalty + 100;    // behind us, must loop
        }
        long farthest = e.farthestStopInDirection();
        return Math.abs(farthest - e.currentFloor())
             + Math.abs(farthest - call.floor())
             + queuePenalty + 50;                    // reverse needed
    }
}

Two design notes the interviewer will probe. First, the score is a sum of physical distance and a small queue penalty, not pure distance; otherwise a car that already has eight stops becomes the cheapest option for a ninth caller and starves everyone. Second, the FAULTED filter is at the top of the loop so a failed car is removed from the fleet in O(1) without any explicit deregistration, which keeps the controller usable during a single-car outage.

Design patterns used#

  • Strategy for the dispatch algorithm. DispatchStrategy has four concrete implementations: NearestCarStrategy (greedy, used in small residential lifts), ScanStrategy and LookStrategy (sweep variants for steady traffic), and DestinationDispatchStrategy (the modern grouping algorithm used in high-rises). Swapping the strategy is a single controller-constructor change.
  • State for the door, motor, and cabin lifecycles. Each is its own enum with its own transition table, and the Elevator simply delegates door.open() / motor.accelerate() to the current state object. Adding a new state (OBSTRUCTED on the door, EARTHQUAKE_HOLD on the cabin) does not modify existing transitions.
  • Command for the request objects. Both CallRequest and FloorRequest are queued per car, can be logged, and can be replayed during a controller restart. This is what lets the controller crash and resume without losing pending hall calls.
  • Observer for sensor notifications. Floor sensors fire FloorArrivalEvent to the controller; weight sensors fire OverloadEvent to the elevator; the door fires ObstructionEvent to both. Subscribers can be added (a real-time dashboard, a fault logger) without modifying the publishers.
  • Singleton for the BuildingDirectory. There is exactly one mapping of floor numbers to physical positions per building, and the controller, the cabin display, and the hall panels all read from the same instance.

Common variants & follow-ups#

  • Destination dispatch flips the boarding model. Riders enter their destination at a lobby panel before boarding, and the controller assigns them to a specific car displayed on the kiosk. This lets the system group passengers with the same destination into one car and cuts average wait times by 25 to 30 percent in tall buildings during morning peak. The strategy is a graph-partition variant of bin-packing.
  • VIP / express modes reserve a car for an executive floor or skip floors 1-30 during a fire drill. Modelled as a RoutingPolicy that the dispatcher consults before scoring; the policy can be activated by a key switch or a remote API call.
  • Fire and emergency override recalls every car to the lobby, locks the doors open, and refuses new calls until an operator clears the alarm. The state machine adds a RECALL cabin state that out-prioritises everything else; the dispatcher's scoring function is bypassed entirely during recall.
  • Peak-traffic optimisation assigns cars to zones (cars 1-2 for floors 1-15, cars 3-4 for floors 16-30) and biases idle parking toward the lobby in the morning and the residential floors at night. The zoning is itself a strategy that can be hot-swapped from a building-management dashboard.
  • Freight + passenger coexistence uses a separate cabin with a wider door and a higher weight limit but the same ElevatorController. Freight requests are tagged so the dispatcher can refuse to send them during passenger peak hours.
  • ML-based predictive dispatch trains on the building's historical call pattern (morning lobby surge, lunch downward rush) and pre-positions idle cars near the floors that will probably call next. Implemented as a PredictiveIdleParkingStrategy that adjusts the parking floor every few minutes.

Edge cases#

  • Simultaneous calls at multiple floors: the controller scores each call independently and may assign the same car to several adjacent calls in the same sweep. The car's stop queue stays sorted by floor in the current direction so the LOOK pattern remains a single traversal rather than a zigzag.
  • Weight overload sensor: the cabin refuses to close the door while load > capacity * 1.10. The door state machine reports OVERLOADED, a chime plays, and the call originally assigned to this car is reassigned to the next-best car so the rider does not block the hall.
  • Door obstruction detection: the photoelectric sensor reports a beam break during CLOSING. The door transitions to OBSTRUCTED, reverses to OPEN, waits a configurable timeout (typically 3 seconds), then retries CLOSING up to N times before flagging a fault to the controller. The motor stays STOPPED throughout so the cabin cannot move with the door open.
  • Idle parking strategy: where does a free car wait? Three common choices: the lobby (good for morning peak), the building median floor (minimises worst-case distance), or a learned floor from historical data. The strategy is pluggable behind IdleParkingStrategy.parkAt(elevator, time) so a building can tune it per shift.
  • Starvation prevention: a CallRequest that has been queued longer than a threshold (commonly 90 seconds) is promoted by bumping its priority field. The dispatcher's scoring function adds a large negative bonus for high-priority calls, which forces the next free car to grab it even if the geometry is unfavourable.
  • Single car offline (graceful degradation): when an elevator faults, the controller's FAULTED filter removes it from scoring and re-enqueues its pending stops onto the remaining fleet. A maintenance ticket is fired via the observer chain. The system continues to dispatch on the reduced fleet, only flagging a building-level error when the second-to-last car also faults.

Glossary & fundamentals#

Concepts referenced in this design. Each row links to its canonical page; the tag column shows whether it is a high-level (HLD) or low-level (LLD) concept.

Tag Concept What it is Page
LLD Behavioural patterns Strategy, Observer, State, Command, Chain behavioral-patterns

Quick reference#

Functional#

  • Hall calls + car calls.
  • Multiple cars dispatched optimally.
  • Door operation, weight, safety.
  • Firefighter / maintenance mode.

Non-functional#

  • Real-time response.
  • Safe by design - fail-secure brakes.

Patterns#

  • State, Strategy, Observer, Command (calls).

Refs#

  • "Elevator Scheduling Algorithms" papers; SCAN / LOOK survey.
  • Otis / Kone engineering whitepapers.

FAQ#

How do you design an elevator dispatcher?#

A central ElevatorSystem owns N Car objects and assigns each floor call to the car that minimises a cost function over distance, current direction, and pending requests.

What scheduling algorithm fits elevator dispatch?#

SCAN (the elevator algorithm) sweeps in one direction servicing every call until no more in that direction, then reverses. It is simple and avoids starvation under steady traffic.

How is the elevator's state modelled?#

Use the State pattern with states like Idle, MovingUp, MovingDown, DoorsOpen, and Maintenance; events such as floor reached or button pressed trigger transitions.

Why use multiple cars instead of one big elevator?#

Multiple cars cut average wait time by serving parallel calls and let you apply zoning, where each car covers a floor range, which reduces interference during peak traffic.

What design patterns are common in elevator LLD?#

State for car status, Strategy for the dispatch algorithm, Command for floor and cabin requests, and Observer for indicator lights and motor controllers.

Video walkthrough

How to Design an Elevator System : via Gaurav Sen