Designing an elevator system
In this series (15 parts)
- Introduction to low level design
- SOLID principles
- Design patterns: Creational
- Design patterns: Structural
- Design patterns: Behavioral
- Designing a parking lot
- Designing a library management system
- Designing an elevator system
- Designing a hotel booking system
- Designing a ride-sharing model
- Designing a rate limiter
- Designing a logging framework
- Designing a notification system
- API design and contract-first development
- Data modeling for system design
An elevator system is one of the most rewarding LLD problems because it forces you to think about real-time state, scheduling under constraints, and multi-entity coordination. The core challenge is not “make a box go up and down.” It is deciding which floor to visit next when ten people press buttons at the same time.
Requirements
Functional:
- A building has N floors and M elevators.
- Each floor has up/down call buttons. Each elevator cabin has floor-selection buttons.
- The system dispatches the optimal elevator to answer a floor call.
- Elevators move in one direction until there are no more requests in that direction, then reverse (LOOK algorithm).
- Doors open automatically on arrival, close after a timeout.
- Emergency stop overrides all scheduling.
Non-functional:
- Minimize average wait time across all passengers.
- The system must handle concurrent button presses safely.
- Adding a new scheduling algorithm should not require rewriting the controller.
Entities
| Entity | Responsibility |
|---|---|
| Elevator | Tracks current floor, direction, door state, requests |
| ElevatorController | Receives external requests, dispatches to elevators |
| Request | Represents a floor call or cabin button press |
| Direction | Enum: UP, DOWN, IDLE |
| DoorState | Enum: OPEN, CLOSED, OPENING, CLOSING |
Class diagram
classDiagram
class ElevatorController {
-List~Elevator~ elevators
-SchedulingStrategy strategy
+handleFloorRequest(Request) void
+handleCabinRequest(int elevatorId, int floor) void
+step() void
}
class Elevator {
-int id
-int currentFloor
-Direction direction
-DoorState doorState
-TreeSet~Integer~ upQueue
-TreeSet~Integer~ downQueue
+addRequest(int floor, Direction dir) void
+move() void
+openDoors() void
+closeDoors() void
+isIdle() boolean
}
class Request {
-int floor
-Direction direction
-long timestamp
}
class SchedulingStrategy {
<<interface>>
+selectElevator(List~Elevator~ elevators, Request request) Elevator
}
class LOOKStrategy {
+selectElevator(List~Elevator~ elevators, Request request) Elevator
}
class ShortestSeekStrategy {
+selectElevator(List~Elevator~ elevators, Request request) Elevator
}
class Direction {
<<enumeration>>
UP
DOWN
IDLE
}
class DoorState {
<<enumeration>>
OPEN
CLOSED
OPENING
CLOSING
}
ElevatorController "1" *-- "many" Elevator
ElevatorController --> SchedulingStrategy
SchedulingStrategy <|.. LOOKStrategy
SchedulingStrategy <|.. ShortestSeekStrategy
Elevator --> Direction
Elevator --> DoorState
ElevatorController ..> Request
Class diagram for the elevator system.
Elevator state machine
An individual elevator cycles through a small set of states. The transitions depend on the request queue and the current floor.
stateDiagram-v2 [*] --> Idle Idle --> MovingUp : request above Idle --> MovingDown : request below Idle --> DoorsOpen : request at current floor MovingUp --> DoorsOpen : arrived at requested floor MovingDown --> DoorsOpen : arrived at requested floor DoorsOpen --> DoorsClosed : timeout DoorsClosed --> MovingUp : more requests above DoorsClosed --> MovingDown : more requests below DoorsClosed --> Idle : no pending requests MovingUp --> EmergencyStop : emergency MovingDown --> EmergencyStop : emergency DoorsOpen --> EmergencyStop : emergency EmergencyStop --> Idle : reset
State diagram for a single elevator.
Notice that DoorsOpen is a transient state. The elevator does not stay there indefinitely; a timeout triggers the transition to DoorsClosed, and the scheduler then decides the next move.
The LOOK algorithm
LOOK is the standard elevator scheduling algorithm. It works like the disk-arm SCAN algorithm but reverses direction when there are no more requests ahead, rather than traveling all the way to the end.
How it works:
- The elevator moves in its current direction.
- At each floor, if there is a request for this floor in the current direction, stop and open doors.
- After closing doors, continue in the same direction if there are more requests ahead.
- If no more requests ahead, reverse direction.
- If no requests in either direction, go idle.
public void move() {
if (direction == Direction.UP) {
Integer next = upQueue.higher(currentFloor);
if (next != null) {
currentFloor = next;
upQueue.remove(next);
openDoors();
} else if (!downQueue.isEmpty()) {
direction = Direction.DOWN;
move();
} else {
direction = Direction.IDLE;
}
} else if (direction == Direction.DOWN) {
Integer next = downQueue.lower(currentFloor);
if (next != null) {
currentFloor = next;
downQueue.remove(next);
openDoors();
} else if (!upQueue.isEmpty()) {
direction = Direction.UP;
move();
} else {
direction = Direction.IDLE;
}
}
}
Using TreeSet for the request queues gives O(log n) insertion and O(log n) nearest-floor lookup via higher() and lower().
Request handling sequence
When someone presses the “Up” button on floor 3, here is the full sequence of events:
sequenceDiagram participant P as Passenger participant FC as FloorCallPanel participant EC as ElevatorController participant S as SchedulingStrategy participant E as Elevator P->>FC: Press UP on floor 3 FC->>EC: handleFloorRequest(floor=3, dir=UP) EC->>S: selectElevator(elevators, request) S-->>EC: elevator #2 EC->>E: addRequest(floor=3, dir=UP) E->>E: move() towards floor 3 E->>E: openDoors() P->>E: Enter cabin, press floor 7 E->>E: addRequest(floor=7, dir=UP) E->>E: closeDoors() E->>E: move() towards floor 7 E->>E: openDoors() P->>E: Exit cabin
Sequence diagram showing a complete ride from floor call to destination.
Scheduling strategies
The Strategy pattern makes it easy to swap scheduling algorithms.
Shortest Seek Time First (SSTF): Pick the elevator closest to the requesting floor. Simple, but can starve distant floors.
public class ShortestSeekStrategy implements SchedulingStrategy {
public Elevator selectElevator(List<Elevator> elevators, Request request) {
return elevators.stream()
.min(Comparator.comparingInt(e ->
Math.abs(e.getCurrentFloor() - request.getFloor())))
.orElseThrow();
}
}
LOOK-aware dispatch: Prefer an elevator already moving toward the requesting floor in the right direction. Fall back to the nearest idle elevator.
public class LOOKStrategy implements SchedulingStrategy {
public Elevator selectElevator(List<Elevator> elevators, Request request) {
// Prefer elevators moving toward the request
Optional<Elevator> moving = elevators.stream()
.filter(e -> isMovingToward(e, request))
.min(Comparator.comparingInt(e ->
Math.abs(e.getCurrentFloor() - request.getFloor())));
if (moving.isPresent()) return moving.get();
// Fall back to nearest idle
return elevators.stream()
.filter(Elevator::isIdle)
.min(Comparator.comparingInt(e ->
Math.abs(e.getCurrentFloor() - request.getFloor())))
.orElse(elevators.get(0));
}
}
Concurrency considerations
Button presses are asynchronous events. Multiple passengers press buttons on different floors simultaneously. The ElevatorController must be thread-safe.
Request queue thread safety: Each elevator’s upQueue and downQueue should use ConcurrentSkipListSet or be guarded by a lock.
Controller dispatch: The handleFloorRequest method should be synchronized or use a concurrent queue that the controller drains on each tick.
Simulation loop: In a real system, the controller runs an event loop. Each tick, it processes pending requests and calls move() on each elevator. This is the Command pattern applied to scheduling.
public void step() {
for (Elevator elevator : elevators) {
if (elevator.isDoorOpen()) {
elevator.closeDoors();
} else {
elevator.move();
}
}
}
Extensibility
| Change | Impact |
|---|---|
| Add express elevators | Subclass Elevator, override canStop(floor) |
| Add weight-based limits | New field on Elevator, check before opening |
| Add VIP priority scheduling | New SchedulingStrategy implementation |
| Add floor access restrictions | Access control list on Elevator or Floor |
What comes next
The hotel booking system shifts from real-time control to transactional design. You will deal with availability calendars, double-booking prevention, and cancellation policies, problems where the data model matters more than the scheduling algorithm.