System Design

Elevator System

20 min

Elevator System

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

  1. Multiple Elevators: Support N elevators in a building
  2. Multiple Floors: Support M floors
  3. Request Handling:
    • External requests (hall buttons): UP/DOWN at each floor
    • Internal requests (cabin buttons): Specific floor destinations
  4. Direction Control: Each elevator moves UP, DOWN, or stays IDLE
  5. Optimal Dispatch: Assign nearest available elevator to requests
  6. Door Control: Open/close doors at floors
  7. Emergency Features: Emergency stop, door sensors

Non-Functional Requirements

  1. Efficiency: Minimize average wait time
  2. Fairness: No passenger starvation
  3. Safety: Handle emergency situations
  4. Scalability: Support adding more elevators
  5. Thread-safe: Handle concurrent requests

Design Patterns Used

  1. Strategy Pattern: Elevator scheduling algorithms
  2. State Pattern: Elevator states (IDLE, MOVING_UP, MOVING_DOWN)
  3. Singleton Pattern: ElevatorController
  4. Observer Pattern: Request notification system
  5. 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 scheduling

2. LOOK

text
1- Like SCAN but reverses when last request reached 2- More efficient than SCAN

3. Nearest Car

text
1- Assign request to elevator with minimum cost 2- Cost = distance + direction compatibility

Advanced 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

AspectDecisionTrade-off
SchedulingNearest CarOptimal wait time vs. complex logic
Data StructurePriority QueuesEfficient floor selection vs. memory
ThreadingOne thread per elevatorRealistic simulation vs. overhead
StateEnum-basedType 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

  1. How to handle elevator breakdown?

    • Mark elevator as MAINTENANCE
    • Redistribute requests to other elevators
    • Send notification to maintenance
  2. How to optimize for energy efficiency?

    • Group nearby requests
    • Reduce unnecessary movements
    • Park idle elevators at strategic floors
  3. How to implement emergency mode?

    • Priority queue for emergency requests
    • Clear all normal requests
    • Direct elevators to emergency floor
  4. 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

Press j for next, k for previous