The Elevator System is one of the most challenging LLD problems because it requires modeling a real-time control system with multiple concurrent actors. It tests your understanding of the State pattern, scheduling algorithms, and clean separation of concerns. Let's break it down completely.
Core Requirements
- Multiple elevators serving a building with multiple floors
- External requests: Up/Down buttons on each floor
- Internal requests: Floor buttons inside the elevator
- Assign the optimal elevator to each request
- Elevator states: Moving Up, Moving Down, Idle
- Door logic: open/close with 5-second safety timeout
- Emergency stop halts all elevators
Core Entities
- Building — has floors and an ElevatorController
- Floor — has Up and Down request buttons
- Elevator — has a state, current floor, and destination queue
- ElevatorController — receives requests, assigns elevators
- Request — either an external (floor button) or internal (panel) request
- Door — manages open/close with timeout
Elevator State Machine
enum ElevatorState { IDLE, MOVING_UP, MOVING_DOWN, DOORS_OPEN }
class Elevator {
currentFloor: number;
state: ElevatorState;
destinationQueue: number[]; // sorted floors to visit
addDestination(floor: number): void {
if (!this.destinationQueue.includes(floor)) {
this.destinationQueue.push(floor);
this.sortQueue(); // SCAN algorithm
}
}
move(): void {
if (this.destinationQueue.length === 0) {
this.state = ElevatorState.IDLE; return;
}
const next = this.destinationQueue[0];
if (next > this.currentFloor) this.state = ElevatorState.MOVING_UP;
if (next < this.currentFloor) this.state = ElevatorState.MOVING_DOWN;
this.currentFloor = next;
this.destinationQueue.shift();
this.openDoors();
}
}Elevator Assignment Strategy
The assignment algorithm is a great place to use the Strategy pattern:
interface ElevatorSelectionStrategy {
select(request: Request, elevators: Elevator[]): Elevator;
}
class NearestElevatorStrategy implements ElevatorSelectionStrategy {
select(request: Request, elevators: Elevator[]): Elevator {
return elevators
.filter(e => e.state !== ElevatorState.DOORS_OPEN)
.sort((a, b) =>
Math.abs(a.currentFloor - request.floor) -
Math.abs(b.currentFloor - request.floor)
)[0];
}
}
class SCANStrategy implements ElevatorSelectionStrategy {
// Assigns elevator already moving in the same direction, if between current and destination
select(request: Request, elevators: Elevator[]): Elevator { ... }
}Command Pattern for Requests
interface ElevatorCommand { execute(elevator: Elevator): void; }
class GoToFloorCommand implements ElevatorCommand {
constructor(private floor: number) {}
execute(elevator: Elevator) { elevator.addDestination(this.floor); }
}
class EmergencyStopCommand implements ElevatorCommand {
execute(elevator: Elevator) { elevator.emergencyStop(); }
}Common Mistakes
- Not using a destination queue — elevators need to serve multiple floors in one trip
- Ignoring direction when assigning — an elevator moving up past floor 3 shouldn't be assigned a downward request from floor 2
- Forgetting door timeout — doors must auto-close, this is a State transition