LESSON 11.4
Walkthrough: elevator system
Design and implement a multi-car elevator control system using Strategy for request dispatching and Observer for floor display updates.
6 min read
The Elevator System is a popular senior LLD problem that tests object decomposition, scheduling algorithms, and decoupled event notification.
1. Requirements
- Entities:
ElevatorCar: current floor, direction (UP,DOWN,IDLE), destination queue.HallRequest: origin floor and requested direction (passenger on floor 4 wanting to go UP).
- Core Patterns:
- Strategy Pattern: Select which elevator car should service an external hall call (e.g. Nearest Car vs Direction-Matching SCAN).
- Observer Pattern: Floor panels and audio chimes observe car arrivals.
2. Architecture Diagram
Laying out the diagram…
3. Implementation
const Direction = { UP: "UP", DOWN: "DOWN", IDLE: "IDLE" };
// Observer: Floor Display
class DisplayObserver {
onFloorArrival(carId, floor) {
console.log(`[Display] Car ${carId} arrived at floor ${floor}`);
}
}
// Elevator Car Entity
class ElevatorCar {
#id;
#currentFloor = 1;
#direction = Direction.IDLE;
#destinations = new Set();
#listeners = [];
constructor(id) {
this.#id = id;
}
addArrivalListener(listener) {
this.#listeners.push(listener);
}
addDestination(floor) {
this.#destinations.add(floor);
if (this.#direction === Direction.IDLE) {
this.#direction = floor > this.#currentFloor ? Direction.UP : Direction.DOWN;
}
}
step() {
if (this.#destinations.size === 0) {
this.#direction = Direction.IDLE;
return;
}
// Move 1 floor in active direction
if (this.#direction === Direction.UP) this.#currentFloor++;
else if (this.#direction === Direction.DOWN) this.#currentFloor--;
// Check arrival
if (this.#destinations.has(this.#currentFloor)) {
this.#destinations.delete(this.#currentFloor);
for (const listener of this.#listeners) {
listener.onFloorArrival(this.#id, this.#currentFloor);
}
}
}
get id() { return this.#id; }
get currentFloor() { return this.#currentFloor; }
get direction() { return this.#direction; }
get targetCount() { return this.#destinations.size; }
}
// Strategy: Dispatch algorithm (finds closest idle or matching direction car)
class NearestCarStrategy {
chooseElevator(cars, requestedFloor, requestedDir) {
let bestCar = null;
let minDistance = Infinity;
for (const car of cars) {
const dist = Math.abs(car.currentFloor - requestedFloor);
// Prefer IDLE cars or cars already moving toward this floor in same direction
const canPick = car.direction === Direction.IDLE || car.direction === requestedDir;
if (canPick && dist < minDistance) {
minDistance = dist;
bestCar = car;
}
}
return bestCar ?? cars[0]; // Fallback to first car
}
}
// System Coordinator
class ElevatorController {
#cars = [];
#strategy;
constructor(cars, strategy = new NearestCarStrategy()) {
this.#cars = cars;
this.#strategy = strategy;
}
requestElevator(floor, direction) {
const car = this.#strategy.chooseElevator(this.#cars, floor, direction);
car.addDestination(floor);
return car.id;
}
tick() {
for (const car of this.#cars) car.step();
}
}
// Verification Test
const car1 = new ElevatorCar("Car-1");
const car2 = new ElevatorCar("Car-2");
const display = new DisplayObserver();
car1.addArrivalListener(display);
const controller = new ElevatorController([car1, car2]);
// Passenger on Floor 3 presses UP:
const assignedId = controller.requestElevator(3, Direction.UP);
console.assert(assignedId === "Car-1");
// Simulate elevator ticks:
controller.tick(); // Moves to floor 2
controller.tick(); // Arrives at floor 3 (Triggers display listener!)
console.assert(car1.currentFloor === 3);
console.log("Current car 1 floor:", car1.currentFloor);public class Example {
enum Direction { UP, DOWN, IDLE }
static final class ElevatorCar {
final String id;
int currentFloor = 1;
Direction direction = Direction.IDLE;
final java.util.Set<Integer> targets = new java.util.HashSet
JavaScript · 114 lines
UTF-8Key Takeaways for Machine Coding
- Keep Entities Focused:
ElevatorCarmanages physical movement;ElevatorControllercoordinates the fleet. - Apply Patterns Naturally: Use Strategy for the dispatch algorithm, and Observer for floor arrival events.
- Verify with Tests: Always end with a clean test simulating multiple calls and ticks.
Try it
Elevator Controller LLD
Implement an elevator bank controller that dispatches calls according to the SCAN scheduling strategy.