LLD for Elevator System: Designing a Smart Lift
How do you design an elevator that serves multiple floors efficiently? We break down the Low-Level Design (LLD) using the State and Strategy patterns.
Abstract AlgorithmsTLDR: An elevator system must manage multiple cars, floors, and concurrent requests using a Dispatch Algorithm to minimize wait time. We use the State Pattern for elevator movement and the Strategy Pattern for dispatching, with the SCAN sweep algorithm at the core.
๐ The Hotel Lift Problem: What We Are Designing
A hotel has 10 floors and 3 elevators. At any moment:
- External requests come in: "Someone on Floor 5 pressed UP."
- Internal requests come in: "Someone inside Lift 1 pressed Floor 10."
- The system must decide which car to dispatch, and which order to serve floors.
This is a classic State Machine + Scheduling design. The key insight is that a lift behaves differently based on its current state โ and we should model that explicitly.
๐ข Domain Model: The Core Entities
classDiagram
class Building {
+List~ParkingFloor~ floors
+List~ElevatorCar~ cars
+Dispatcher dispatcher
}
class ElevatorCar {
+int id
+int currentFloor
+Direction direction
+ElevatorState state
+TreeSet~Integer~ stops
+handleRequest(Request r)
+move()
}
class Dispatcher {
+ElevatorCar assign(Request r)
}
class Request {
+int sourceFloor
+Direction direction
}
Building --> ElevatorCar
Building --> Dispatcher
Dispatcher --> ElevatorCar
- ElevatorCar: Holds a sorted set of destination floors. Has a
state(IDLE, MOVING_UP, MOVING_DOWN). - Dispatcher: The brain. Assigns incoming
Requestto the best car. - Request: Either
ExternalRequest(floor + direction pressed outside) orInternalRequest(destination floor pressed inside the car).
โ๏ธ The SCAN Algorithm: Why Not First-Come-First-Served?
FCFS is naive: if requests come from floors 10, 1, 10, the lift zigzags 1โ10โ1โ10 (4 ร 9 = 36 floors traveled).
The SCAN Algorithm (also called Elevator Algorithm):
- Move in the current direction (UP) serving all stops along the way.
- When no more stops in that direction, reverse.
Toy example: Lift at Floor 4, moving UP. Requests: Floor 6 (UP), Floor 2 (DOWN), Floor 8 (UP).
| Step | Lift Position | Action |
| 1 | Floor 4 โ 6 | Serve Floor 6 (on the way UP) |
| 2 | Floor 6 โ 8 | Serve Floor 8 (on the way UP) |
| 3 | Floor 8 (switch) | No more UP requests โ reverse to DOWN |
| 4 | Floor 8 โ 2 | Serve Floor 2 on the way DOWN |
Total travel: 6 floors instead of 18 (FCFS). This is why real elevators use SCAN variants.
๐ง State Pattern: Modeling Elevator Movement
An elevator's behavior depends on its current state. We make this explicit with the State pattern:
public interface ElevatorState {
void handleRequest(ElevatorCar car, int targetFloor);
String name();
}
public class IdleState implements ElevatorState {
public void handleRequest(ElevatorCar car, int targetFloor) {
car.addStop(targetFloor);
if (targetFloor > car.getCurrentFloor()) {
car.setState(new MovingUpState());
} else if (targetFloor < car.getCurrentFloor()) {
car.setState(new MovingDownState());
}
}
public String name() { return "IDLE"; }
}
public class MovingUpState implements ElevatorState {
public void handleRequest(ElevatorCar car, int targetFloor) {
// SCAN: only add if it's above current floor
if (targetFloor >= car.getCurrentFloor()) {
car.addStop(targetFloor); // serve on the way
} else {
car.deferRequest(targetFloor); // serve on the next sweep
}
}
public String name() { return "MOVING_UP"; }
}
The state transitions:
stateDiagram-v2
[*] --> IDLE
IDLE --> MOVING_UP : request above current floor
IDLE --> MOVING_DOWN : request below current floor
MOVING_UP --> IDLE : no more up requests
MOVING_UP --> MOVING_DOWN : switch at top
MOVING_DOWN --> IDLE : no more down requests
MOVING_DOWN --> MOVING_UP : switch at bottom
โ๏ธ Dispatcher: Strategy Pattern for Assignment Policy
The Dispatcher uses the Strategy pattern so we can swap assignment algorithms:
public interface DispatchStrategy {
ElevatorCar select(List<ElevatorCar> cars, Request request);
}
// Nearest-Car strategy: pick the car that will reach the requester soonest
public class NearestCarStrategy implements DispatchStrategy {
public ElevatorCar select(List<ElevatorCar> cars, Request request) {
return cars.stream()
.filter(c -> c.canService(request))
.min(Comparator.comparingInt(c ->
Math.abs(c.getCurrentFloor() - request.getSourceFloor())))
.orElse(null);
}
}
canService() checks the car's state:
- IDLE โ always serviceable.
- MOVING_UP โ serviceable if request is above current floor and direction matches.
- MOVING_DOWN โ serviceable if request is below current floor and direction matches.
โ๏ธ Design Patterns Applied
| Pattern | Where | Why |
| State | ElevatorCar.state | Behavior changes based on direction; avoids if (IDLE) {...} else if (MOVING_UP) {...} |
| Strategy | Dispatcher | Swap between Nearest-Car, Least-Loaded, and Zone-Based dispatch without changing the Dispatcher class |
| Command | Request objects | Encapsulate a floor request; supports queuing and undo (open-door requests) |
| Observer | Floor display panels | Subscribe to ElevatorCar.currentFloor updates to show position |
๐ Summary
- State Pattern models elevator direction changes as first-class state transitions โ no branching spaghetti.
- SCAN Algorithm minimizes travel distance by sweeping in one direction before reversing.
- Strategy Pattern in the Dispatcher keeps assignment policy pluggable.
- Concurrent requests from multiple floors are handled via per-car
TreeSet<Integer>of stops, which is auto-sorted.
๐ Practice Quiz
Why is FCFS (First Come First Served) a poor dispatch strategy for elevators?
- A) It requires more memory.
- B) It causes zigzag travel that maximizes floors traveled; SCAN sweeps in one direction, minimizing waste.
- C) FCFS can't handle requests from multiple floors.
Answer: B
In the State Pattern for elevator movement, what happens when a request arrives for a floor below the current floor while the car is MOVING_UP?
- A) The car immediately reverses to MOVING_DOWN.
- B) The request is deferred to the next downward sweep โ the car continues upward first.
- C) The request is rejected.
Answer: B
What is the advantage of using a
TreeSet<Integer>to store pending stops in anElevatorCar?- A) It prevents duplicates and keeps stops automatically sorted by floor number, enabling in-order serving.
- B) It uses less memory than a list.
- C) It allows thread-safe concurrent modification without locks.
Answer: A

Written by
Abstract Algorithms
@abstractalgorithms
More Posts
System Design Service Discovery and Health Checks: Routing Traffic to Healthy Instances
TLDR: Service discovery is how clients find the right service instance at runtime, and health checks are how systems decide whether an instance should receive traffic. Together, they turn dynamic infrastructure from guesswork into deterministic routi...
System Design Roadmap: A Complete Learning Path from Basics to Advanced Architecture
TLDR: This roadmap organizes every system-design-tagged post in this repository into learning groups and a recommended order. It is designed for interview prep and practical architecture thinking, from fundamentals to scaling, reliability, and implem...
System Design Observability, SLOs, and Incident Response: Operating Systems You Can Trust
TLDR: Observability is how you understand system behavior from telemetry, SLOs are explicit reliability targets, and incident response is the execution model when those targets are at risk. Together, they convert operational chaos into measurable, re...
System Design Message Queues and Event-Driven Architecture: Building Reliable Asynchronous Systems
TLDR: Message queues and event-driven architecture let services communicate asynchronously, absorb bursty traffic, and isolate failures. The core design challenge is not adding a queue. It is defining delivery semantics, retry behavior, and idempoten...
