Elevator System - Low-Level Design
Problem Statement
Design an elevator control system for a multi-floor building that efficiently handles passenger requests while optimizing wait time and energy consumption.
Requirements
Functional Requirements
- Multiple Elevators: Support N elevators in a building
- Multiple Floors: Support M floors
- Request Handling:
- External requests (hall buttons): UP/DOWN at each floor
- Internal requests (cabin buttons): Specific floor destinations
- Direction Control: Each elevator moves UP, DOWN, or stays IDLE
- Optimal Dispatch: Assign nearest available elevator to requests
- Door Control: Open/close doors at floors
- Emergency Features: Emergency stop, door sensors
Non-Functional Requirements
- Efficiency: Minimize average wait time
- Fairness: No passenger starvation
- Safety: Handle emergency situations
- Scalability: Support adding more elevators
- Thread-safe: Handle concurrent requests
Design Patterns Used
- Strategy Pattern: Elevator scheduling algorithms
- State Pattern: Elevator states (IDLE, MOVING_UP, MOVING_DOWN)
- Singleton Pattern: ElevatorController
- Observer Pattern: Request notification system
- Factory Pattern: Request creation
Class Diagram
Main Class
+ properties
+ attributes
+ methods()
+ operations()
Helper Class
+ fields
+ data
+ helpers()
+ utilities()
Interface
«interface»
+ abstract methods()
━━━▶
Dependency
◆━━━
Composition
△━━━
Inheritance
Core Enums
java
1public enum Direction {
2 UP, DOWN, IDLE
3}
4
5public enum ElevatorState {
6 IDLE, MOVING_UP, MOVING_DOWN, DOOR_OPEN, MAINTENANCE
7}
8
9public enum RequestType {
10 EXTERNAL, // Hall button
11 INTERNAL // Cabin button
12}Request Class
java
1public class Request {
2 private final int floor;
3 private final Direction direction;
4 private final RequestType type;
5 private final long timestamp;
6
7 public Request(int floor, Direction direction, RequestType type) {
8 this.floor = floor;
9 this.direction = direction;
10 this.type = type;
11 this.timestamp = System.currentTimeMillis();
12 }
13
14 // Getters
15 public int getFloor() { return floor; }
16 public Direction getDirection() { return direction; }
17 public RequestType getType() { return type; }
18 public long getTimestamp() { return timestamp; }
19
20 @Override
21 public boolean equals(Object o) {
22 if (this == o) return true;
23 if (o == null || getClass() != o.getClass()) return false;
24 Request request = (Request) o;
25 return floor == request.floor &&
26 direction == request.direction;
27 }
28
29 @Override
30 public int hashCode() {
31 return Objects.hash(floor, direction);
32 }
33}Elevator Class
java
1public class Elevator {
2 private final int elevatorId;
3 private int currentFloor;
4 private Direction currentDirection;
5 private ElevatorState state;
6
7 // Priority queues for efficient floor selection
8 private final PriorityQueue<Integer> upQueue; // Min heap
9 private final PriorityQueue<Integer> downQueue; // Max heap
10
11 private final Set<Integer> currentRequests;
12 private final int minFloor;
13 private final int maxFloor;
14
15 public Elevator(int elevatorId, int minFloor, int maxFloor) {
16 this.elevatorId = elevatorId;
17 this.currentFloor = 0; // Ground floor
18 this.currentDirection = Direction.IDLE;
19 this.state = ElevatorState.IDLE;
20 this.minFloor = minFloor;
21 this.maxFloor = maxFloor;
22
23 // Min heap for upward movement (ascending order)
24 this.upQueue = new PriorityQueue<>();
25
26 // Max heap for downward movement (descending order)
27 this.downQueue = new PriorityQueue<>(Collections.reverseOrder());
28
29 this.currentRequests = new HashSet<>();
30 }
31
32 /**
33 * Add a destination floor to the elevator's queue
34 */
35 public synchronized void addDestination(int floor, Direction requestDirection) {
36 if (floor < minFloor || floor > maxFloor) {
37 throw new IllegalArgumentException("Invalid floor: " + floor);
38 }
39
40 if (floor == currentFloor) {
41 return; // Already at floor
42 }
43
44 currentRequests.add(floor);
45
46 // Add to appropriate queue based on request direction
47 if (floor > currentFloor) {
48 upQueue.offer(floor);
49 } else {
50 downQueue.offer(floor);
51 }
52
53 // Start moving if idle
54 if (state == ElevatorState.IDLE) {
55 processNextDestination();
56 }
57 }
58
59 /**
60 * Move elevator and handle requests
61 */
62 public synchronized void move() {
63 if (state == ElevatorState.MAINTENANCE) {
64 return;
65 }
66
67 // Check if reached destination
68 if (hasReachedDestination()) {
69 handleArrival();
70 return;
71 }
72
73 // Continue moving in current direction
74 if (currentDirection == Direction.UP) {
75 currentFloor++;
76 state = ElevatorState.MOVING_UP;
77 } else if (currentDirection == Direction.DOWN) {
78 currentFloor--;
79 state = ElevatorState.MOVING_DOWN;
80 }
81
82 System.out.println("Elevator " + elevatorId +
83 " at floor " + currentFloor +
84 " moving " + currentDirection);
85 }
86
87 private boolean hasReachedDestination() {
88 return currentRequests.contains(currentFloor);
89 }
90
91 private void handleArrival() {
92 System.out.println("Elevator " + elevatorId +
93 " arrived at floor " + currentFloor);
94
95 state = ElevatorState.DOOR_OPEN;
96 currentRequests.remove(currentFloor);
97
98 // Remove from appropriate queue
99 upQueue.remove(currentFloor);
100 downQueue.remove(currentFloor);
101
102 // Simulate door open/close time
103 try {
104 Thread.sleep(2000); // 2 seconds
105 } catch (InterruptedException e) {
106 Thread.currentThread().interrupt();
107 }
108
109 // Process next destination
110 processNextDestination();
111 }
112
113 private void processNextDestination() {
114 // Check current direction queue first
115 if (currentDirection == Direction.UP && !upQueue.isEmpty()) {
116 currentDirection = Direction.UP;
117 state = ElevatorState.MOVING_UP;
118 } else if (currentDirection == Direction.DOWN && !downQueue.isEmpty()) {
119 currentDirection = Direction.DOWN;
120 state = ElevatorState.MOVING_DOWN;
121 }
122 // Switch direction if current queue empty
123 else if (!upQueue.isEmpty()) {
124 currentDirection = Direction.UP;
125 state = ElevatorState.MOVING_UP;
126 } else if (!downQueue.isEmpty()) {
127 currentDirection = Direction.DOWN;
128 state = ElevatorState.MOVING_DOWN;
129 }
130 // No more requests
131 else {
132 currentDirection = Direction.IDLE;
133 state = ElevatorState.IDLE;
134 System.out.println("Elevator " + elevatorId + " is now IDLE");
135 }
136 }
137
138 /**
139 * Calculate cost to serve a request (for scheduling)
140 * Lower cost = better candidate
141 */
142 public int calculateCost(Request request) {
143 int targetFloor = request.getFloor();
144 Direction requestDir = request.getDirection();
145
146 // If elevator is idle, cost is just the distance
147 if (state == ElevatorState.IDLE) {
148 return Math.abs(currentFloor - targetFloor);
149 }
150
151 // If elevator is moving in same direction and request is on the way
152 if (currentDirection == requestDir) {
153 if ((currentDirection == Direction.UP &&
154 targetFloor >= currentFloor) ||
155 (currentDirection == Direction.DOWN &&
156 targetFloor <= currentFloor)) {
157 return Math.abs(currentFloor - targetFloor);
158 }
159 }
160
161 // Otherwise, elevator needs to finish current direction first
162 // High cost to discourage assignment
163 return Integer.MAX_VALUE / 2;
164 }
165
166 public boolean isIdle() {
167 return state == ElevatorState.IDLE;
168 }
169
170 // Getters
171 public int getElevatorId() { return elevatorId; }
172 public int getCurrentFloor() { return currentFloor; }
173 public Direction getCurrentDirection() { return currentDirection; }
174 public ElevatorState getState() { return state; }
175}Elevator Controller (Singleton)
java
1public class ElevatorController {
2 private static ElevatorController instance;
3
4 private final List<Elevator> elevators;
5 private final Queue<Request> pendingRequests;
6 private final ElevatorScheduler scheduler;
7 private final ExecutorService executorService;
8
9 private final int minFloor;
10 private final int maxFloor;
11
12 private ElevatorController(int numElevators, int minFloor, int maxFloor) {
13 this.minFloor = minFloor;
14 this.maxFloor = maxFloor;
15 this.elevators = new ArrayList<>();
16 this.pendingRequests = new ConcurrentLinkedQueue<>();
17 this.scheduler = new NearestCarScheduler();
18
19 // Initialize elevators
20 for (int i = 0; i < numElevators; i++) {
21 elevators.add(new Elevator(i, minFloor, maxFloor));
22 }
23
24 // Thread pool for elevator movement
25 this.executorService = Executors.newFixedThreadPool(numElevators);
26
27 // Start elevator threads
28 startElevators();
29 }
30
31 public static synchronized ElevatorController getInstance(
32 int numElevators, int minFloor, int maxFloor) {
33 if (instance == null) {
34 instance = new ElevatorController(numElevators, minFloor, maxFloor);
35 }
36 return instance;
37 }
38
39 /**
40 * Handle external request (hall button)
41 */
42 public void requestElevator(int floor, Direction direction) {
43 Request request = new Request(floor, direction, RequestType.EXTERNAL);
44 System.out.println("External request: Floor " + floor +
45 " Direction " + direction);
46
47 Elevator bestElevator = scheduler.selectElevator(elevators, request);
48
49 if (bestElevator != null) {
50 bestElevator.addDestination(floor, direction);
51 } else {
52 pendingRequests.offer(request);
53 }
54 }
55
56 /**
57 * Handle internal request (cabin button)
58 */
59 public void selectFloor(int elevatorId, int floor) {
60 if (elevatorId < 0 || elevatorId >= elevators.size()) {
61 throw new IllegalArgumentException("Invalid elevator ID");
62 }
63
64 Elevator elevator = elevators.get(elevatorId);
65 Direction direction = floor > elevator.getCurrentFloor() ?
66 Direction.UP : Direction.DOWN;
67
68 System.out.println("Internal request: Elevator " + elevatorId +
69 " to floor " + floor);
70
71 elevator.addDestination(floor, direction);
72 }
73
74 private void startElevators() {
75 for (Elevator elevator : elevators) {
76 executorService.submit(() -> {
77 while (!Thread.currentThread().isInterrupted()) {
78 elevator.move();
79 try {
80 Thread.sleep(1000); // 1 second per floor
81 } catch (InterruptedException e) {
82 Thread.currentThread().interrupt();
83 break;
84 }
85 }
86 });
87 }
88 }
89
90 public void displayStatus() {
91 System.out.println("\n=== Elevator Status ===");
92 for (Elevator elevator : elevators) {
93 System.out.println("Elevator " + elevator.getElevatorId() +
94 ": Floor " + elevator.getCurrentFloor() +
95 " | State: " + elevator.getState() +
96 " | Direction: " + elevator.getCurrentDirection());
97 }
98 System.out.println("=======================\n");
99 }
100
101 public void shutdown() {
102 executorService.shutdown();
103 }
104}Scheduling Strategy (Strategy Pattern)
java
1public interface ElevatorScheduler {
2 Elevator selectElevator(List<Elevator> elevators, Request request);
3}
4
5/**
6 * Nearest Car Algorithm
7 * Assigns request to elevator with lowest cost
8 */
9public class NearestCarScheduler implements ElevatorScheduler {
10 @Override
11 public Elevator selectElevator(List<Elevator> elevators, Request request) {
12 Elevator bestElevator = null;
13 int minCost = Integer.MAX_VALUE;
14
15 for (Elevator elevator : elevators) {
16 int cost = elevator.calculateCost(request);
17 if (cost < minCost) {
18 minCost = cost;
19 bestElevator = elevator;
20 }
21 }
22
23 return bestElevator;
24 }
25}
26
27/**
28 * Round Robin Scheduler
29 * Distributes load evenly
30 */
31public class RoundRobinScheduler implements ElevatorScheduler {
32 private int lastAssigned = -1;
33
34 @Override
35 public synchronized Elevator selectElevator(
36 List<Elevator> elevators, Request request) {
37 lastAssigned = (lastAssigned + 1) % elevators.size();
38 return elevators.get(lastAssigned);
39 }
40}Usage Example
java
1public class ElevatorSystemDemo {
2 public static void main(String[] args) throws InterruptedException {
3 // Initialize: 3 elevators, floors 0-10
4 ElevatorController controller =
5 ElevatorController.getInstance(3, 0, 10);
6
7 // Simulate requests
8 controller.requestElevator(5, Direction.UP); // Person at floor 5 going up
9 Thread.sleep(1000);
10
11 controller.requestElevator(3, Direction.DOWN); // Person at floor 3 going down
12 Thread.sleep(1000);
13
14 controller.selectFloor(0, 8); // Person in elevator 0 selects floor 8
15 Thread.sleep(2000);
16
17 controller.displayStatus();
18
19 // More requests
20 controller.requestElevator(7, Direction.UP);
21 controller.requestElevator(2, Direction.DOWN);
22 controller.selectFloor(1, 6);
23
24 Thread.sleep(5000);
25 controller.displayStatus();
26
27 // Shutdown
28 Thread.sleep(20000);
29 controller.shutdown();
30 }
31}Scheduling Algorithms
1. SCAN (Elevator Algorithm)
text
1- Move in one direction until no more requests
2- Then reverse direction
3- Similar to disk scheduling2. LOOK
text
1- Like SCAN but reverses when last request reached
2- More efficient than SCAN3. Nearest Car
text
1- Assign request to elevator with minimum cost
2- Cost = distance + direction compatibilityAdvanced Features
1. Destination Control System (DCS)
java
1// Users select destination at hall, not just direction
2public void advancedRequest(int currentFloor, int destinationFloor) {
3 Direction direction = destinationFloor > currentFloor ?
4 Direction.UP : Direction.DOWN;
5
6 // Group passengers going to similar floors
7 // Assign specific elevator
8 Elevator assignedElevator =
9 selectOptimalElevator(currentFloor, destinationFloor, direction);
10
11 System.out.println("Please board Elevator " +
12 assignedElevator.getElevatorId());
13}2. Peak Traffic Handling
java
1// Morning rush: Send all elevators to ground floor
2// Evening rush: Distribute across building
3public void handlePeakHours(PeakType peakType) {
4 if (peakType == PeakType.MORNING_RUSH) {
5 for (Elevator elevator : elevators) {
6 if (elevator.isIdle()) {
7 elevator.addDestination(0, Direction.IDLE);
8 }
9 }
10 }
11}Testing
java
1@Test
2public void testSingleRequest() {
3 ElevatorController controller =
4 ElevatorController.getInstance(2, 0, 10);
5
6 controller.requestElevator(5, Direction.UP);
7
8 // Wait for elevator to reach
9 Thread.sleep(6000);
10
11 // Verify elevator at floor 5
12 assertTrue(isAnyElevatorAtFloor(5));
13}
14
15@Test
16public void testMultipleRequests() {
17 controller.requestElevator(3, Direction.UP);
18 controller.requestElevator(7, Direction.DOWN);
19 controller.requestElevator(5, Direction.UP);
20
21 // Verify all requests eventually served
22 // No starvation
23}Trade-offs
| Aspect | Decision | Trade-off |
|---|---|---|
| Scheduling | Nearest Car | Optimal wait time vs. complex logic |
| Data Structure | Priority Queues | Efficient floor selection vs. memory |
| Threading | One thread per elevator | Realistic simulation vs. overhead |
| State | Enum-based | Type safety vs. flexibility |
💡 Interview Tips & Out-of-the-Box Thinking
Common Pitfalls
- Not considering direction: Elevator going up shouldn't pick down requests (violates user expectations)
- Starvation problem: Upper floors never serviced if ground floor keeps requesting
- Concurrent modification: Multiple threads modifying request queue without synchronization
- Not handling emergency stop: Fire alarm should immediately change behavior
Design Pattern Recognition
- Strategy Pattern: Different scheduling algorithms (Nearest Car, SCAN, LOOK, Zoning)
- State Pattern: Elevator states (IDLE, MOVING_UP, MOVING_DOWN, DOOR_OPEN, MAINTENANCE)
- Singleton Pattern: Single controller coordinating all elevators
- Observer Pattern: Notify display panels when elevator arrives
Advanced Considerations
- Starvation prevention: Aging algorithm - increase priority of long-waiting requests
- Energy optimization: Park idle elevators at predicted high-demand floors (ML-based)
- Zone-based dispatch: High-rise buildings split into zones to reduce travel time
- Double-deck elevators: Two cabins stacked - serve even/odd floors simultaneously
- Destination dispatch: User enters destination floor at lobby (not in cabin) for optimal grouping
Creative Solutions
- Predictive dispatching: During morning rush, pre-position elevators at ground floor
- Express elevators: Non-stop to top floors during peak hours
- AI-based learning: Learn building traffic patterns and adapt scheduling
- Priority lanes: VIP floors get faster service
- Group dispatch optimization: Assign multiple requests to single elevator based on direction
Trade-off Discussions
- FCFS vs Optimization: Fair but inefficient vs Optimal but complex
- Centralized vs Decentralized: Single controller (easier coordination) vs Each elevator independent (fault tolerant)
- Real-time vs Batch: Immediate response vs Group requests for efficiency
- Simple vs Smart: Nearest car (simple, good enough) vs Machine learning (complex, optimal)
Edge Cases to Mention
- All elevators on same floor: How to distribute next request? (Answer: Round-robin or load balancing)
- Request from current floor: Should doors reopen if recently closed? (Answer: Timeout threshold)
- Opposite direction request: Elevator at floor 5 going up, user at 5 wants down (Answer: Skip, serve on return)
- Emergency override: Fire alarm triggers - all elevators to ground floor (Answer: Clear queue, priority mode)
- Maximum weight exceeded: Doors keep reopening (Answer: Force people to exit, audio warning)
- Maintenance mode during peak: One elevator down during rush hour (Answer: Redistribute load, notify users)
Follow-up Questions
-
How to handle elevator breakdown?
- Mark elevator as MAINTENANCE
- Redistribute requests to other elevators
- Send notification to maintenance
-
How to optimize for energy efficiency?
- Group nearby requests
- Reduce unnecessary movements
- Park idle elevators at strategic floors
-
How to implement emergency mode?
- Priority queue for emergency requests
- Clear all normal requests
- Direct elevators to emergency floor
-
How to handle overweight scenario?
- Weight sensors in cabin
- Reject new internal requests if overweight
- Alert to reduce load
Class Diagram
Elevator System Class Diagram
Detailed class/schema diagram will be displayed here