Definition
- Elevator – a car with a current floor, direction (UP, DOWN, IDLE), and sorted request sets for each direction.
- ElevatorController – receives external hall requests and dispatches them to the most suitable car using a scoring policy.
- SCAN/LOOK algorithm – the car serves all stops in its current direction, then reverses, preventing starvation.
- State machine – each car moves through IDLE, MOVING, DOORS_OPEN, and DOORS_CLOSED states with explicit transitions.
- Weight limit – the car refuses to move while overloaded, keeping doors open and firing an alert.
Picture the multi-floor headquarters of National Bank of India. When Rajesh on floor 2 presses "up" and Priya on floor 8 presses "down", a smart controller decides which of several lifts should serve each person so nobody waits too long. Each lift is like a careful teller: it finishes the customers in its current direction before reversing, opens and closes its doors safely, and refuses to move if overloaded.
Understanding the Concepts
What are the core entities?
The core entities are Elevator, ElevatorController, Request, and Direction.
package com.bank.elevator.model;
public enum Direction {
UP, DOWN, IDLE
}
package com.bank.elevator.model;
public class Request {
private final int floor;
private final Direction direction;
public Request(int floor, Direction direction) {
this.floor = floor;
this.direction = direction;
}
public int getFloor() { return floor; }
public Direction getDirection() { return direction; }
}
How does each elevator car work?
Each Elevator tracks its current floor, direction, and two sorted sets of stops: one for the up journey and one for the down journey.
package com.bank.elevator.model;
import java.util.TreeSet;
public class Elevator {
private final String carId;
private int currentFloor;
private Direction direction;
private ElevatorState state;
private final TreeSet<Integer> upStops;
private final TreeSet<Integer> downStops;
private final int maxWeight;
private int currentWeight;
public Elevator(String carId, int maxWeight) {
this.carId = carId;
this.currentFloor = 0;
this.direction = Direction.IDLE;
this.state = ElevatorState.IDLE;
this.upStops = new TreeSet<>();
this.downStops = new TreeSet<>();
this.maxWeight = maxWeight;
this.currentWeight = 0;
}
public void addStop(int floor) {
if (floor > currentFloor) {
upStops.add(floor);
} else if (floor < currentFloor) {
downStops.add(floor);
}
}
public void move() {
if (direction == Direction.UP && !upStops.isEmpty()) {
currentFloor = upStops.pollFirst();
} else if (direction == Direction.DOWN && !downStops.isEmpty()) {
currentFloor = downStops.pollLast();
}
if (upStops.isEmpty() && downStops.isEmpty()) {
direction = Direction.IDLE;
state = ElevatorState.IDLE;
} else if (upStops.isEmpty()) {
direction = Direction.DOWN;
} else if (downStops.isEmpty()) {
direction = Direction.UP;
}
System.out.println("Car " + carId + " at floor " + currentFloor
+ ", direction: " + direction);
}
public boolean isOverweight(int additionalWeight) {
return (currentWeight + additionalWeight) > maxWeight;
}
public int getCurrentFloor() { return currentFloor; }
public Direction getDirection() { return direction; }
public String getCarId() { return carId; }
public ElevatorState getState() { return state; }
}
How does the SCAN/LOOK algorithm work?
The SCAN algorithm means the car keeps serving requests in its current direction until none remain, then reverses. This is efficient and avoids starvation, where a far-away request waits forever.
Car at floor 3, going UP, stops: {5, 7, 9}
1. Move to 5 (serve stop)
2. Move to 7 (serve stop)
3. Move to 9 (serve stop)
4. No more up stops → reverse to DOWN
5. Serve down stops: {6, 2}
6. Move to 6, then 2
7. No more down stops → IDLE
How does the controller dispatch requests?
The ElevatorController receives external hall requests and picks the best car using a scoring policy.
package com.bank.elevator.service;
import com.bank.elevator.model.*;
import java.util.List;
public class ElevatorController {
private final List<Elevator> cars;
public ElevatorController(List<Elevator> cars) {
this.cars = cars;
}
public Elevator dispatch(Request request) {
Elevator best = null;
int bestScore = Integer.MAX_VALUE;
for (Elevator car : cars) {
int score = score(car, request);
if (score < bestScore) {
bestScore = score;
best = car;
}
}
if (best == null) {
throw new IllegalStateException("No elevator available");
}
best.addStop(request.getFloor());
System.out.println("Dispatched " + best.getCarId()
+ " to floor " + request.getFloor());
return best;
}
private int score(Elevator car, Request request) {
int distance = Math.abs(
car.getCurrentFloor() - request.getFloor());
// Prefer idle cars or cars already heading the right way
if (car.getDirection() == Direction.IDLE) {
return distance;
}
if (car.getDirection() == request.getDirection()
&& isOnTheWay(car, request)) {
return distance;
}
return distance + 100; // penalise wrong direction
}
private boolean isOnTheWay(Elevator car, Request request) {
if (car.getDirection() == Direction.UP) {
return request.getFloor() > car.getCurrentFloor();
}
return request.getFloor() < car.getCurrentFloor();
}
}
How is the elevator modelled as a state machine?
Each car is an explicit state machine to prevent unsafe boolean-flag race conditions.
package com.bank.elevator.model;
public enum ElevatorState {
IDLE,
MOVING,
DOORS_OPEN,
DOORS_CLOSED
}
IDLE ──request──→ DOORS_OPEN ──close──→ DOORS_CLOSED ──move──→ MOVING
▲ │
│ │
└──────────── no more stops ←── DOORS_OPEN ←── arrived ────────┘
The car refuses to move while doors are open. If boarding would exceed the weight limit, it keeps doors open and raises an alert rather than moving unsafely.
How do you handle weight limits?
The car tracks current weight and checks before closing doors.
public void board(int passengerWeight) {
if (isOverweight(passengerWeight)) {
System.out.println("ALERT: Weight limit exceeded on "
+ carId + ". Doors remain open.");
return;
}
currentWeight += passengerWeight;
}
What about extensibility?
The scheduling algorithm is a pluggable Strategy. The controller can switch between nearest-car, SCAN, and LOOK without changing its structure.
package com.bank.elevator.service;
import com.bank.elevator.model.*;
import java.util.List;
public interface DispatchStrategy {
Elevator selectCar(List<Elevator> cars, Request request);
}
What should a senior engineer know?
- Model doors, weight checks, and motion as explicit states, not boolean flags. Implicit flags produce unsafe race conditions in real hardware control.
- The SCAN algorithm prevents starvation. A simple nearest-car policy can starve far-away floors in a busy building.
- In a real system, the controller runs on a dedicated thread and processes requests from a concurrent queue.
- For multiple elevator banks (express, local), use separate controllers with different floor ranges.
What are common interview follow-up questions?
- How would you handle a VIP express elevator that skips certain floors?
- How do you prevent starvation of requests on distant floors?
- What happens if an elevator is stuck between floors?
- How would you add a maintenance mode for one car?
- How do you scale to a building with 50 floors and 10 elevators?
Visual Overview
Elevator State Machine:
───────────────────────
IDLE ──request──→ DOORS_OPEN ──close──→ DOORS_CLOSED
▲ ▲ │
│ │ │ move
│ arrived ▼
└── no stops ──── MOVING ←───────── DOORS_CLOSED
SCAN Algorithm:
───────────────
Car at floor 3, going UP
9 ← stop
7 ← stop
5 ← stop
>>> 3 (current)
2 ← down stop
Serves 5, 7, 9 (UP), then reverses, serves 2 (DOWN)
Dispatch Scoring: ───────────────── Request: Floor 5, UP Car A: Floor 3, UP → score = 2 (on the way) ← BEST Car B: Floor 8, DOWN → score = 103 (wrong direction) Car C: Floor 1, IDLE → score = 4 (idle, close)
Key Takeaway
When you design an elevator system, separate two concerns: the controller that decides which car serves a request, and each car that decides how to move. Each elevator car tracks its current floor, direction, and separate sorted sets of stops for the up and down journeys, and it runs as an explicit state machine moving between idle, moving, doors open, and doors closed.
For motion, the SCAN or LOOK algorithm means a car keeps serving every request in its current direction until there are none left, and only then reverses. This is efficient and prevents starvation, which matters in a busy building like National Bank of India headquarters where a far-away floor must never wait indefinitely.
For dispatch, the controller scores every car by distance to the request and direction compatibility. An idle car nearby or a car already heading toward the caller wins. A car heading the opposite direction is penalised. The scheduling algorithm is a pluggable Strategy so the system can switch policies without rewriting the controller.
Two safety concerns to always model explicitly are doors and weight. The car refuses to move while doors are open, and if boarding exceeds the weight limit it keeps doors open and raises an alert. The senior insight is that an elevator is fundamentally a safety-critical state machine, so designing the states and transitions first and treating scheduling as a pluggable strategy on top is the right decomposition.
Remember one sentence: an elevator system dispatches requests to the nearest direction-compatible car and runs each car as a strict state machine that never moves with doors open or weight exceeded.
---