problemhardooddesign-an-elevator-systemdesign an elevator systemdesignanelevatorsystemood-for-elevator-systemood for elevator systemoodforelevatorsystem

OOD - Elevator System

HardUpdated: Jan 1, 2026

1. Problem Statement

Design a clear, multi‑elevator system for a building. The controller assigns elevators to hall calls, handles car calls, moves cars between floors, and operates doors safely.

There are two request types: hall calls (floor + direction) and car calls (destination). Each elevator is a simple state machine (IDLE, MOVING_UP, MOVING_DOWN, DOOR_OPENING/OPEN/CLOSING, MAINTENANCE, EMERGENCY). Scheduling should prefer nearby elevators already moving in the requested direction; other requests are queued.

Core goals:

  • Minimize average wait time with a reasonable policy (e.g., SCAN/LOOK)
  • Enforce safety: never move with doors open; reopen on obstruction
  • Respect capacity limits; support maintenance and emergency stop
  • Scale to multiple floors and multiple elevator cars

2. System Requirements

Functional Requirements:

  1. Support multiple elevators (2-10) operating independently in a building
  2. Handle hall calls: passengers request elevator from floor with Up/Down direction
  3. Handle car calls: passengers inside elevator select destination floors
  4. Implement scheduling algorithm to assign hall calls to appropriate elevators
  5. Move elevators between floors in response to requests
  6. Open and close doors at appropriate floors with safety checks
  7. Prevent door closure if obstructed (sensor detection)
  8. Track current floor, direction, and state for each elevator
  9. Enforce maximum capacity limits (reject boarding if overloaded)
  10. Support emergency stop with alarm activation
  11. Handle maintenance mode (take elevator offline)
  12. Display current floor and direction indicators
  13. Support configurable building floors (e.g., -2 to 50)
  14. Queue and prioritize requests (internal car calls prioritized over hall calls)

Non-Functional Requirements:

  1. Safety: Doors must not close on passengers; elevators must not move with doors open
  2. Performance: Average wait time <30 seconds during normal load
  3. Reliability: System must handle elevator failures gracefully (reassign requests)
  4. Scalability: Support buildings with 2-100 floors and 2-20 elevators
  5. Concurrency: Thread-safe operation for simultaneous button presses and elevator movements

Assumptions:

  • One elevator car per shaft (no multi-car shafts)
  • Passengers select destination after boarding (not destination dispatch initially)
  • Elevators have uniform speed and acceleration
  • Floor-to-floor travel time is constant (simplified physics)
  • No express zones or service floors initially

3. Use Case Diagram

Actors:

  • Passenger: Requests elevator and selects destination
  • Building Operator: Monitors system, sets maintenance mode
  • Elevator Controller: Autonomous scheduling and coordination
  • Safety System: Monitors sensors and triggers emergency procedures

Core Use Cases:

  • Call Elevator (Hall Call)
  • Select Destination Floor (Car Call)
  • Monitor Elevator Status
  • Set Maintenance Mode
  • Trigger Emergency Stop
  • Handle Door Obstruction
  • Display Floor Indicator
  • Optimize Scheduling
  • Handle Overload
  • Redistribute Requests on Failure
graph TB
    subgraph ElevatorSystem["Elevator System"]
        UC1["Call Elevator Hall Call"]
        UC2["Select Destination Car Call"]
        UC3["Move Between Floors"]
        UC4["Open/Close Doors"]
        UC5["Monitor Status"]
        UC6["Set Maintenance Mode"]
        UC7["Emergency Stop"]
        UC8["Handle Obstruction"]
        UC9["Handle Overload"]
        UC10["Redistribute Requests"]
    end
    
    Passenger([Passenger])
    Operator([Building Operator])
    Controller([Elevator Controller])
    Safety([Safety System])
    
    Passenger --> UC1
    Passenger --> UC2
    
    Operator --> UC5
    Operator --> UC6
    Operator --> UC7
    
    Controller --> UC3
    Controller --> UC4
    Controller --> UC10
    
    Safety --> UC8
    Safety --> UC9
    
    UC1 -.->|triggers| UC3
    UC2 -.->|triggers| UC3
    UC3 -.->|arrival triggers| UC4
    UC4 -.->|if obstructed| UC8
    UC6 -.->|triggers| UC10
    UC7 -.->|triggers| UC4
    
    style Passenger fill:#4CAF50,color:#fff
    style Operator fill:#FF9800,color:#fff
    style Controller fill:#2196F3,color:#fff
    style Safety fill:#F44336,color:#fff

4. Class Diagram

Core Classes:

  • Direction: Enum (UP, DOWN, IDLE)
  • ElevatorState: Enum (IDLE, MOVING_UP, MOVING_DOWN, DOOR_OPENING, DOOR_OPEN, DOOR_CLOSING, MAINTENANCE, EMERGENCY)
  • Request: Abstract base for hall and car calls with floor, timestamp, priority
  • HallRequest: External request from floor with direction (UP/DOWN)
  • CarRequest: Internal request for destination floor
  • Door: Manages door state with safety interlocks and obstruction detection
  • Elevator: State machine managing movement, requests, passengers, capacity
  • ElevatorController: Orchestrates multiple elevators, implements scheduling
  • Scheduler (Strategy): Interface for scheduling algorithms (SCAN, LOOK, etc.)
  • Display: Shows current floor and direction to passengers
classDiagram
    class Direction {
        <<enumeration>>
        UP
        DOWN
        IDLE
    }
    
    class ElevatorState {
        <<enumeration>>
        IDLE
        MOVING_UP
        MOVING_DOWN
        DOOR_OPENING
        DOOR_OPEN
        DOOR_CLOSING
        MAINTENANCE
        EMERGENCY
    }
    
    class Request {
        <<abstract>>
        -String id
        -int floor
        -long timestamp
        -int priority
        +Request(int)
        +getFloor() int
        +getTimestamp() long
        +getPriority() int
    }
    
    class HallRequest {
        -Direction direction
        +HallRequest(int, Direction)
        +getDirection() Direction
    }
    
    class CarRequest {
        +CarRequest(int)
    }
    
    class Door {
        -boolean open
        -boolean obstructed
        +Door()
        +open() void
        +close() boolean
        +isOpen() boolean
        +setObstructed(boolean) void
        +isObstructed() boolean
    }
    
    class Elevator {
        -int id
        -int currentFloor
        -int minFloor
        -int maxFloor
        -Direction direction
        -ElevatorState state
        -Door door
        -Queue~Integer~ destinationQueue
        -int capacity
        -int currentLoad
        +Elevator(int, int, int, int)
        +move() void
        +addDestination(int) void
        +openDoor() void
        +closeDoor() void
        +stop() void
        +setMaintenance(boolean) void
        +canAcceptRequest(HallRequest) boolean
        +getCurrentFloor() int
        +getDirection() Direction
        +getState() ElevatorState
    }
    
    class Scheduler {
        <<interface>>
        +selectElevator(HallRequest, List~Elevator~) Elevator
    }
    
    class SCANScheduler {
        +selectElevator(HallRequest, List~Elevator~) Elevator
    }
    
    class ElevatorController {
        -List~Elevator~ elevators
        -Queue~HallRequest~ pendingRequests
        -Scheduler scheduler
        -int minFloor
        -int maxFloor
        +ElevatorController(int, int, int)
        +requestElevator(int, Direction) void
        +selectDestination(int, int) void
        +processRequests() void
        +setElevatorMaintenance(int, boolean) void
        +getSystemStatus() String
    }
    
    class Display {
        -int elevatorId
        +Display(int)
        +updateFloor(int) void
        +updateDirection(Direction) void
        +showMessage(String) void
    }
    
    Request <|-- HallRequest
    Request <|-- CarRequest
    
    Elevator --> Direction
    Elevator --> ElevatorState
    Elevator "1" --> "1" Door
    Elevator --> Display
    
    ElevatorController "1" *-- "*" Elevator
    ElevatorController --> HallRequest
    ElevatorController --> Scheduler
    
    Scheduler <|.. SCANScheduler

5. Activity Diagrams

Hall Call Processing

graph TD
    A([Passenger presses Up/Down button]) --> B[Create HallRequest]
    B --> C[Controller receives request]
    C --> D[Scheduler.selectElevator]
    D --> E{Suitable elevator found?}
    
    E -->|Yes| F[Assign request to elevator]
    F --> G[Elevator adds floor to destination queue]
    G --> H{Elevator on same floor going same direction?}
    H -->|Yes| I[Open doors immediately]
    H -->|No| J[Move toward floor]
    J --> K[Arrive at floor]
    K --> I
    I --> L[Passenger boards]
    L --> M([Passenger selects destination])
    
    E -->|No| N[Add to pending requests queue]
    N --> O[Wait for elevator availability]
    O --> D

Elevator Movement Cycle

graph TD
    Start([Elevator IDLE with destinations queued]) --> Check{Destinations exist?}
    Check -->|No| Idle[Remain IDLE]
    
    Check -->|Yes| DetermineDir[Determine direction to next destination]
    DetermineDir --> SetState[Set state to MOVING_UP or MOVING_DOWN]
    SetState --> Move[Move one floor]
    Move --> Update[Update currentFloor]
    Update --> Arrival{Arrived at destination?}
    
    Arrival -->|No| Move
    Arrival -->|Yes| DoorOpen[Change state to DOOR_OPENING]
    DoorOpen --> OpenDoor[Open doors]
    OpenDoor --> DoorOpenState[State: DOOR_OPEN]
    DoorOpenState --> Wait[Wait for passengers]
    Wait --> DoorClose[Change state to DOOR_CLOSING]
    DoorClose --> Obstruct{Door obstructed?}
    
    Obstruct -->|Yes| Reopen[Reopen doors]
    Reopen --> DoorOpenState
    
    Obstruct -->|No| CloseDoor[Close doors successfully]
    CloseDoor --> RemoveDest[Remove destination from queue]
    RemoveDest --> Check

Emergency Stop Handling

graph TD
    A([Emergency button pressed]) --> B[Set state to EMERGENCY]
    B --> C[Stop movement immediately]
    C --> D{Currently on a floor?}
    
    D -->|Yes| E[Open doors]
    E --> F[Activate alarm]
    F --> G[Clear destination queue]
    
    D -->|No| H[Move to nearest floor]
    H --> E
    
    G --> I[Notify controller]
    I --> J[Controller redistributes pending requests]
    J --> K([Elevator awaits reset by operator])

6. Java Implementation

Enums

/**
 * Direction of travel
 */
public enum Direction {
    UP,
    DOWN,
    IDLE;
    
    public Direction opposite() {
        if (this == UP) return DOWN;
        if (this == DOWN) return UP;
        return IDLE;
    }
}

/**
 * Elevator state machine states
 */
public enum ElevatorState {
    IDLE,           // Stationary, doors closed, no requests
    MOVING_UP,      // Moving upward
    MOVING_DOWN,    // Moving downward
    DOOR_OPENING,   // Doors in process of opening
    DOOR_OPEN,      // Doors fully open, passengers can enter/exit
    DOOR_CLOSING,   // Doors in process of closing
    MAINTENANCE,    // Offline for service
    EMERGENCY;      // Emergency stop activated
    
    public boolean isMoving() {
        return this == MOVING_UP || this == MOVING_DOWN;
    }
    
    public boolean canAcceptPassengers() {
        return this == DOOR_OPEN;
    }
}

Request Classes

import java.util.UUID;

/**
 * Abstract base class for elevator requests
 */
public abstract class Request implements Comparable<Request> {
    protected final String id;
    protected final int floor;
    protected final long timestamp;
    protected int priority; // Higher number = higher priority
    
    public Request(int floor) {
        this.id = UUID.randomUUID().toString().substring(0, 8);
        this.floor = floor;
        this.timestamp = System.currentTimeMillis();
        this.priority = 0;
    }
    
    public String getId() {
        return id;
    }
    
    public int getFloor() {
        return floor;
    }
    
    public long getTimestamp() {
        return timestamp;
    }
    
    public int getPriority() {
        return priority;
    }
    
    public void setPriority(int priority) {
        this.priority = priority;
    }
    
    /**
     * Age of request in seconds
     */
    public long getAgeSeconds() {
        return (System.currentTimeMillis() - timestamp) / 1000;
    }
    
    @Override
    public int compareTo(Request other) {
        // Higher priority first, then older requests
        int priorityCompare = Integer.compare(other.priority, this.priority);
        if (priorityCompare != 0) return priorityCompare;
        return Long.compare(this.timestamp, other.timestamp);
    }
}

/**
 * External hall call from a floor
 */
public class HallRequest extends Request {
    private final Direction direction;
    
    public HallRequest(int floor, Direction direction) {
        super(floor);
        if (direction == Direction.IDLE) {
            throw new IllegalArgumentException("Hall request must specify UP or DOWN");
        }
        this.direction = direction;
    }
    
    public Direction getDirection() {
        return direction;
    }
    
    @Override
    public String toString() {
        return String.format("HallRequest[floor=%d, dir=%s, age=%ds]", 
            floor, direction, getAgeSeconds());
    }
}

/**
 * Internal car call for destination floor
 */
public class CarRequest extends Request {
    public CarRequest(int floor) {
        super(floor);
        this.priority = 10; // Car calls have higher priority than hall calls
    }
    
    @Override
    public String toString() {
        return String.format("CarRequest[floor=%d]", floor);
    }
}

Door Class

/**
 * Elevator door with safety interlocks
 */
public class Door {
    private boolean open;
    private boolean obstructed;
    
    public Door() {
        this.open = false;
        this.obstructed = false;
    }
    
    /**
     * Open the door
     */
    public synchronized void open() {
        this.open = true;
        System.out.println("  πŸšͺ Doors opening...");
    }
    
    /**
     * Attempt to close the door
     * @return true if closed successfully, false if obstructed
     */
    public synchronized boolean close() {
        if (obstructed) {
            System.out.println("  ⚠️  Door obstruction detected! Reopening...");
            this.open = true;
            return false;
        }
        
        this.open = false;
        System.out.println("  πŸšͺ Doors closing...");
        return true;
    }
    
    public synchronized boolean isOpen() {
        return open;
    }
    
    public synchronized void setObstructed(boolean obstructed) {
        this.obstructed = obstructed;
    }
    
    public synchronized boolean isObstructed() {
        return obstructed;
    }
}

Elevator Class

import java.util.*;
import java.util.concurrent.PriorityQueue;

/**
 * Individual elevator car with state machine
 */
public class Elevator {
    private final int id;
    private int currentFloor;
    private final int minFloor;
    private final int maxFloor;
    private Direction direction;
    private ElevatorState state;
    private final Door door;
    private final Queue<Integer> destinationQueue;
    private final int capacity; // Maximum number of passengers
    private int currentLoad;
    
    public Elevator(int id, int minFloor, int maxFloor, int capacity) {
        this.id = id;
        this.currentFloor = 0; // Start at ground floor
        this.minFloor = minFloor;
        this.maxFloor = maxFloor;
        this.direction = Direction.IDLE;
        this.state = ElevatorState.IDLE;
        this.door = new Door();
        this.destinationQueue = new PriorityQueue<>();
        this.capacity = capacity;
        this.currentLoad = 0;
    }
    
    public int getId() {
        return id;
    }
    
    public synchronized int getCurrentFloor() {
        return currentFloor;
    }
    
    public synchronized Direction getDirection() {
        return direction;
    }
    
    public synchronized ElevatorState getState() {
        return state;
    }
    
    public synchronized boolean isFull() {
        return currentLoad >= capacity;
    }
    
    /**
     * Add destination floor to queue
     */
    public synchronized void addDestination(int floor) {
        if (floor < minFloor || floor > maxFloor) {
            System.out.println("  ❌ Invalid floor: " + floor);
            return;
        }
        
        if (floor == currentFloor) {
            System.out.println("  Already at floor " + floor);
            return;
        }
        
        if (!destinationQueue.contains(floor)) {
            destinationQueue.offer(floor);
            System.out.println("  βœ“ Elevator " + id + " added floor " + floor + " to queue");
        }
    }
    
    /**
     * Move elevator one step (called by controller periodically)
     */
    public synchronized void move() {
        if (state == ElevatorState.MAINTENANCE || state == ElevatorState.EMERGENCY) {
            return; // Don't move if in maintenance or emergency
        }
        
        if (destinationQueue.isEmpty()) {
            if (state != ElevatorState.IDLE) {
                state = ElevatorState.IDLE;
                direction = Direction.IDLE;
                System.out.println("πŸ›‘ Elevator " + id + " now IDLE at floor " + currentFloor);
            }
            return;
        }
        
        // Determine next destination
        int nextFloor = findNextDestination();
        
        if (nextFloor == currentFloor) {
            // Arrived at destination
            arriveAtFloor();
        } else if (nextFloor > currentFloor) {
            // Move up
            if (state != ElevatorState.MOVING_UP) {
                state = ElevatorState.MOVING_UP;
                direction = Direction.UP;
            }
            currentFloor++;
            System.out.println("⬆️  Elevator " + id + " moving UP to floor " + currentFloor);
            
            // Check if we should stop here
            if (destinationQueue.contains(currentFloor)) {
                arriveAtFloor();
            }
        } else {
            // Move down
            if (state != ElevatorState.MOVING_DOWN) {
                state = ElevatorState.MOVING_DOWN;
                direction = Direction.DOWN;
            }
            currentFloor--;
            System.out.println("⬇️  Elevator " + id + " moving DOWN to floor " + currentFloor);
            
            // Check if we should stop here
            if (destinationQueue.contains(currentFloor)) {
                arriveAtFloor();
            }
        }
    }
    
    /**
     * Find next destination using SCAN algorithm
     */
    private int findNextDestination() {
        if (destinationQueue.isEmpty()) {
            return currentFloor;
        }
        
        // Simple approach: take closest destination in current direction
        // More sophisticated: SCAN algorithm continuing in current direction
        
        List<Integer> destinations = new ArrayList<>(destinationQueue);
        Collections.sort(destinations);
        
        if (direction == Direction.UP || direction == Direction.IDLE) {
            // Find smallest destination >= currentFloor
            for (int dest : destinations) {
                if (dest >= currentFloor) {
                    return dest;
                }
            }
        }
        
        if (direction == Direction.DOWN || direction == Direction.IDLE) {
            // Find largest destination <= currentFloor
            for (int i = destinations.size() - 1; i >= 0; i--) {
                if (destinations.get(i) <= currentFloor) {
                    return destinations.get(i);
                }
            }
        }
        
        // Fallback: return any destination
        return destinations.get(0);
    }
    
    /**
     * Handle arrival at a destination floor
     */
    private void arriveAtFloor() {
        System.out.println("πŸ“ Elevator " + id + " arrived at floor " + currentFloor);
        destinationQueue.remove(currentFloor);
        openDoor();
        
        // Simulate door open time
        try {
            Thread.sleep(2000); // 2 seconds for passengers
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        closeDoor();
    }
    
    /**
     * Open doors
     */
    public synchronized void openDoor() {
        state = ElevatorState.DOOR_OPENING;
        door.open();
        state = ElevatorState.DOOR_OPEN;
    }
    
    /**
     * Close doors with obstruction check
     */
    public synchronized void closeDoor() {
        state = ElevatorState.DOOR_CLOSING;
        
        if (!door.close()) {
            // Obstruction detected, reopen
            state = ElevatorState.DOOR_OPEN;
        } else {
            // Successfully closed
            state = destinationQueue.isEmpty() ? ElevatorState.IDLE : 
                   (direction == Direction.UP ? ElevatorState.MOVING_UP : ElevatorState.MOVING_DOWN);
        }
    }
    
    /**
     * Emergency stop
     */
    public synchronized void stop() {
        System.out.println("🚨 EMERGENCY STOP - Elevator " + id);
        state = ElevatorState.EMERGENCY;
        direction = Direction.IDLE;
        
        // Open doors if on a floor
        if (currentFloor >= minFloor && currentFloor <= maxFloor) {
            door.open();
        }
        
        // Clear destinations
        destinationQueue.clear();
    }
    
    /**
     * Set maintenance mode
     */
    public synchronized void setMaintenance(boolean maintenance) {
        if (maintenance) {
            System.out.println("πŸ”§ Elevator " + id + " entering MAINTENANCE mode");
            state = ElevatorState.MAINTENANCE;
            direction = Direction.IDLE;
            destinationQueue.clear();
        } else {
            System.out.println("βœ“ Elevator " + id + " exiting MAINTENANCE mode");
            state = ElevatorState.IDLE;
        }
    }
    
    /**
     * Check if elevator can accept a hall request
     */
    public synchronized boolean canAcceptRequest(HallRequest request) {
        if (state == ElevatorState.MAINTENANCE || state == ElevatorState.EMERGENCY) {
            return false;
        }
        
        if (isFull()) {
            return false;
        }
        
        // Idle elevators can accept any request
        if (state == ElevatorState.IDLE) {
            return true;
        }
        
        // Moving elevators can accept if going same direction and haven't passed floor
        if (request.getDirection() == direction) {
            if (direction == Direction.UP && request.getFloor() >= currentFloor) {
                return true;
            }
            if (direction == Direction.DOWN && request.getFloor() <= currentFloor) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * Calculate distance/cost to serve a request (for scheduling)
     */
    public synchronized int costToServe(HallRequest request) {
        if (!canAcceptRequest(request)) {
            return Integer.MAX_VALUE;
        }
        
        if (state == ElevatorState.IDLE) {
            return Math.abs(currentFloor - request.getFloor());
        }
        
        // If moving in same direction
        if (request.getDirection() == direction) {
            if ((direction == Direction.UP && request.getFloor() >= currentFloor) ||
                (direction == Direction.DOWN && request.getFloor() <= currentFloor)) {
                return Math.abs(currentFloor - request.getFloor());
            }
        }
        
        return Integer.MAX_VALUE;
    }
    
    @Override
    public String toString() {
        return String.format("Elevator[id=%d, floor=%d, dir=%s, state=%s, queue=%d]",
            id, currentFloor, direction, state, destinationQueue.size());
    }
}

Scheduler Interface and Implementation

import java.util.List;

/**
 * Strategy interface for elevator scheduling algorithms
 */
public interface Scheduler {
    /**
     * Select best elevator for a hall request
     * @return selected elevator or null if none suitable
     */
    Elevator selectElevator(HallRequest request, List<Elevator> elevators);
}

/**
 * SCAN-based scheduling algorithm
 */
public class SCANScheduler implements Scheduler {
    
    @Override
    public Elevator selectElevator(HallRequest request, List<Elevator> elevators) {
        Elevator bestElevator = null;
        int minCost = Integer.MAX_VALUE;
        
        for (Elevator elevator : elevators) {
            int cost = elevator.costToServe(request);
            
            if (cost < minCost) {
                minCost = cost;
                bestElevator = elevator;
            } else if (cost == minCost && bestElevator != null) {
                // Tie-breaker: choose elevator with fewer destinations
                if (elevator.getState() == ElevatorState.IDLE && 
                    bestElevator.getState() != ElevatorState.IDLE) {
                    bestElevator = elevator;
                }
            }
        }
        
        return bestElevator;
    }
}

Elevator Controller

import java.util.*;
import java.util.concurrent.*;

/**
 * Central controller managing multiple elevators
 */
public class ElevatorController {
    private final List<Elevator> elevators;
    private final Queue<HallRequest> pendingRequests;
    private final Scheduler scheduler;
    private final int minFloor;
    private final int maxFloor;
    private final ScheduledExecutorService executor;
    
    public ElevatorController(int numElevators, int minFloor, int maxFloor) {
        this.elevators = new ArrayList<>();
        this.minFloor = minFloor;
        this.maxFloor = maxFloor;
        this.pendingRequests = new ConcurrentLinkedQueue<>();
        this.scheduler = new SCANScheduler();
        this.executor = Executors.newScheduledThreadPool(numElevators + 1);
        
        // Create elevators
        for (int i = 0; i < numElevators; i++) {
            Elevator elevator = new Elevator(i + 1, minFloor, maxFloor, 10);
            elevators.add(elevator);
        }
        
        // Start elevator movement threads
        for (Elevator elevator : elevators) {
            executor.scheduleAtFixedRate(() -> elevator.move(), 0, 1, TimeUnit.SECONDS);
        }
        
        // Start request processing thread
        executor.scheduleAtFixedRate(this::processRequests, 0, 500, TimeUnit.MILLISECONDS);
    }
    
    /**
     * Request elevator from a floor (hall call)
     */
    public void requestElevator(int floor, Direction direction) {
        if (floor < minFloor || floor > maxFloor) {
            System.out.println("❌ Invalid floor: " + floor);
            return;
        }
        
        HallRequest request = new HallRequest(floor, direction);
        System.out.println("πŸ“ž Hall call: " + request);
        
        Elevator elevator = scheduler.selectElevator(request, elevators);
        
        if (elevator != null) {
            elevator.addDestination(floor);
        } else {
            pendingRequests.offer(request);
            System.out.println("⏸  Request queued (no suitable elevator)");
        }
    }
    
    /**
     * Select destination floor from inside elevator (car call)
     */
    public void selectDestination(int elevatorId, int floor) {
        Elevator elevator = getElevator(elevatorId);
        if (elevator != null) {
            elevator.addDestination(floor);
        }
    }
    
    /**
     * Process pending requests periodically
     */
    private void processRequests() {
        if (pendingRequests.isEmpty()) {
            return;
        }
        
        Iterator<HallRequest> iterator = pendingRequests.iterator();
        while (iterator.hasNext()) {
            HallRequest request = iterator.next();
            
            Elevator elevator = scheduler.selectElevator(request, elevators);
            if (elevator != null) {
                System.out.println("βœ“ Assigning pending request to Elevator " + elevator.getId());
                elevator.addDestination(request.getFloor());
                iterator.remove();
            }
        }
    }
    
    /**
     * Set elevator maintenance mode
     */
    public void setElevatorMaintenance(int elevatorId, boolean maintenance) {
        Elevator elevator = getElevator(elevatorId);
        if (elevator != null) {
            elevator.setMaintenance(maintenance);
        }
    }
    
    /**
     * Emergency stop for specific elevator
     */
    public void emergencyStop(int elevatorId) {
        Elevator elevator = getElevator(elevatorId);
        if (elevator != null) {
            elevator.stop();
        }
    }
    
    private Elevator getElevator(int elevatorId) {
        for (Elevator elevator : elevators) {
            if (elevator.getId() == elevatorId) {
                return elevator;
            }
        }
        return null;
    }
    
    /**
     * Get system status
     */
    public String getSystemStatus() {
        StringBuilder sb = new StringBuilder();
        sb.append("\n╔════════════════════════════════════════════════╗\n");
        sb.append("β•‘        ELEVATOR SYSTEM STATUS                 β•‘\n");
        sb.append("╠════════════════════════════════════════════════╣\n");
        
        for (Elevator elevator : elevators) {
            sb.append(String.format("β•‘ Elevator %d: Floor %-2d %-12s %-8s β•‘\n",
                elevator.getId(),
                elevator.getCurrentFloor(),
                elevator.getDirection(),
                elevator.getState()));
        }
        
        sb.append("╠════════════════════════════════════════════════╣\n");
        sb.append(String.format("β•‘ Pending Requests: %-28d β•‘\n", pendingRequests.size()));
        sb.append("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•\n");
        
        return sb.toString();
    }
    
    /**
     * Shutdown controller
     */
    public void shutdown() {
        executor.shutdownNow();
    }
}

Demo Application

/**
 * Demo showing elevator system operation
 */
public class ElevatorSystemDemo {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("═══════════════════════════════════════════════");
        System.out.println("      ELEVATOR SYSTEM SIMULATION");
        System.out.println("═══════════════════════════════════════════════\n");
        
        // Create controller with 3 elevators for building with floors 0-10
        ElevatorController controller = new ElevatorController(3, 0, 10);
        
        System.out.println("β–Ά System initialized with 3 elevators\n");
        Thread.sleep(1000);
        
        System.out.println(controller.getSystemStatus());
        
        // Simulate hall calls
        System.out.println("β–Ά Simulating passenger requests...\n");
        
        controller.requestElevator(5, Direction.UP);
        Thread.sleep(1000);
        
        controller.requestElevator(2, Direction.DOWN);
        Thread.sleep(1000);
        
        controller.requestElevator(8, Direction.DOWN);
        Thread.sleep(1000);
        
        System.out.println(controller.getSystemStatus());
        
        // Wait for elevators to move
        System.out.println("β–Ά Elevators moving to pickup floors...\n");
        Thread.sleep(6000);
        
        System.out.println(controller.getSystemStatus());
        
        // Simulate passengers selecting destinations
        System.out.println("\nβ–Ά Passengers selecting destination floors...\n");
        controller.selectDestination(1, 9);  // Elevator 1: go to floor 9
        controller.selectDestination(2, 0);  // Elevator 2: go to ground floor
        controller.selectDestination(3, 3);  // Elevator 3: go to floor 3
        
        Thread.sleep(8000);
        
        System.out.println(controller.getSystemStatus());
        
        // Simulate emergency
        System.out.println("\nβ–Ά Emergency stop on Elevator 2...\n");
        controller.emergencyStop(2);
        Thread.sleep(2000);
        
        System.out.println(controller.getSystemStatus());
        
        // Simulate maintenance
        System.out.println("\nβ–Ά Elevator 1 entering maintenance...\n");
        controller.setElevatorMaintenance(1, true);
        Thread.sleep(1000);
        
        System.out.println(controller.getSystemStatus());
        
        // More requests
        System.out.println("\nβ–Ά New requests with limited elevators...\n");
        controller.requestElevator(7, Direction.DOWN);
        controller.requestElevator(4, Direction.UP);
        
        Thread.sleep(8000);
        
        System.out.println("\nβ–Ά Final Status:");
        System.out.println(controller.getSystemStatus());
        
        controller.shutdown();
        System.out.println("Simulation complete! 🏒");
    }
}

7. Design Patterns Applied

1. State Pattern (Elevator State Machine)

Intent: Represent elevator states explicitly and manage transitions.

Implementation:

  • ElevatorState enum defines states
  • Elevator.state tracks current state
  • Methods enforce valid state transitions

Benefits:

  • Clear semantics for elevator behavior
  • Prevents invalid transitions (e.g., move while doors open)
  • Easy to add new states
if (state == ElevatorState.DOOR_OPEN) {
    // Cannot move while doors open
    return;
}
state = ElevatorState.MOVING_UP;

2. Strategy Pattern (Scheduler)

Intent: Encapsulate scheduling algorithms (SCAN, LOOK, etc.) as interchangeable strategies.

Implementation:

  • Scheduler interface defines contract
  • SCANScheduler implements SCAN algorithm
  • Controller uses strategy polymorphically

Benefits:

  • Easy to swap scheduling algorithms
  • Can A/B test different strategies
  • Algorithm complexity isolated from controller
Scheduler scheduler = new SCANScheduler();
Elevator best = scheduler.selectElevator(request, elevators);

3. Command Pattern (Request Abstraction)

Intent: Encapsulate requests as objects with metadata.

Implementation:

  • Request base class with timestamp, priority
  • HallRequest and CarRequest subclasses
  • Requests are queued and processed asynchronously

Benefits:

  • Requests can be logged, queued, undone
  • Metadata like timestamp enables aging/priority
  • Uniform handling of different request types
Request request = new HallRequest(floor, direction);
pendingRequests.offer(request);

4. Observer Pattern (Request Processing - Implicit)

Intent: Controller observes elevator state changes and assigns new requests.

Could Enhance:

interface ElevatorObserver {
    void onStateChange(Elevator elevator, ElevatorState newState);
}

// Controller observes elevators becoming IDLE
// and automatically assigns pending requests

5. Factory Pattern (Elevator Creation)

Intent: Centralize elevator creation with consistent configuration.

Implementation:

for (int i = 0; i < numElevators; i++) {
    Elevator elevator = new Elevator(i + 1, minFloor, maxFloor, capacity);
    elevators.add(elevator);
}

6. Singleton Pattern (ElevatorController - Optional)

Intent: Single controller instance per building.

Could Implement:

public class ElevatorController {
    private static ElevatorController instance;
    
    public static synchronized ElevatorController getInstance() {
        if (instance == null) {
            instance = new ElevatorController(3, 0, 10);
        }
        return instance;
    }
}

8. Key Design Decisions

Decision 1: State Machine with Explicit States

Rationale: Elevator behavior is inherently state-dependent (can't move with doors open, etc.).

Benefits:

  • Clear state transitions
  • Safety enforcement at state level
  • Easy to visualize and debug

States: IDLE, MOVING_UP, MOVING_DOWN, DOOR_OPENING, DOOR_OPEN, DOOR_CLOSING, MAINTENANCE, EMERGENCY

Decision 2: Separate Hall and Car Requests

Rationale: Different characteristics: hall requests have direction, car requests have higher priority.

Benefits:

  • Type safety for request handling
  • Can prioritize car calls over hall calls
  • Clear domain modeling
HallRequest hallCall = new HallRequest(5, Direction.UP);
CarRequest carCall = new CarRequest(9); // Higher priority

Decision 3: Queue-Based Destination Management

Rationale: Elevators maintain queue of destination floors to visit.

Benefits:

  • Natural FIFO ordering
  • Easy to add/remove destinations
  • Supports SCAN algorithm traversal

Trade-off: Simple queue doesn't optimize order; more sophisticated: sorted set or priority queue.

Decision 4: Scheduler Strategy Pattern

Rationale: Different buildings may need different scheduling algorithms.

Benefits:

  • Pluggable algorithms (SCAN, LOOK, Destination Dispatch)
  • A/B testing different strategies
  • Complexity isolated

Algorithms:

  • SCAN: Continue in direction until no requests ahead
  • LOOK: Reverse when no requests ahead (no need to reach top/bottom)
  • Nearest First: Simple distance-based

Decision 5: Periodic Movement Updates

Rationale: move() called periodically (e.g., every second) to simulate floor-to-floor travel.

Benefits:

  • Simple time-based simulation
  • Easy to adjust speed (change period)
  • Asynchronous concurrent elevator movements
executor.scheduleAtFixedRate(() -> elevator.move(), 0, 1, TimeUnit.SECONDS);

Decision 6: Door Obstruction Handling

Rationale: Door.close() returns false if obstructed, elevator reopens.

Benefits:

  • Safety: prevents closing on passengers
  • Simple boolean interface
  • Could extend with sensors
if (!door.close()) {
    // Obstruction detected
    state = ElevatorState.DOOR_OPEN;
}

Decision 7: Cost-Based Elevator Selection

Rationale: costToServe() calculates distance/time for elevator to serve request.

Benefits:

  • Quantitative comparison of elevators
  • Balances load across elevators
  • Extensible cost function (add energy, wear)

Cost Factors:

  • Distance to request floor
  • Direction compatibility
  • Current load

Decision 8: Pending Request Queue

Rationale: Requests that can't be immediately assigned are queued.

Benefits:

  • No requests lost
  • Periodic retry as elevators become available
  • Fairness via FIFO
if (elevator != null) {
    elevator.addDestination(floor);
} else {
    pendingRequests.offer(request);
}

Decision 9: Emergency and Maintenance States

Rationale: Dedicated states for offline scenarios.

Benefits:

  • Clear semantics (elevator unavailable)
  • Can redistribute pending requests
  • Emergency opens doors if on floor
public void stop() {
    state = ElevatorState.EMERGENCY;
    if (currentFloor >= minFloor && currentFloor <= maxFloor) {
        door.open();
    }
    destinationQueue.clear();
}

Decision 10: Concurrent Execution Model

Rationale: ScheduledExecutorService runs elevators and request processor concurrently.

Benefits:

  • Realistic concurrent elevator movements
  • Non-blocking request handling
  • Scalable to many elevators

Threads:

  • One thread per elevator for movement
  • One thread for request processing
executor.scheduleAtFixedRate(() -> elevator.move(), 0, 1, TimeUnit.SECONDS);
executor.scheduleAtFixedRate(this::processRequests, 0, 500, TimeUnit.MILLISECONDS);

This design creates a robust elevator system with proper state management, intelligent scheduling, safety interlocks, and concurrent operation. The architecture supports evolution to more sophisticated algorithms like Destination Dispatch, energy optimization, predictive scheduling based on usage patterns, and integration with building management systems.

9. Additional Considerations

Functional Requirements:

  • Represent elevators with state (direction, current floor, door status) and handle floor requests.
  • A Bank/ElevatorController that receives floor calls and schedules them to active elevators.
  • Support passenger boarding/unboarding and tracking of onboard passengers and load.
  • Allow alarm and door control signals (open/close), and graceful handling of maintenance mode.

Non-Functional Requirements:

  • Safety: doors must not close on passengers; alarm behavior must be deterministic.
  • Responsiveness: minimize wait time via reasonable scheduling heuristics.
  • Extensibility: support different scheduling strategies and elevator capacities.

2. Use Case Diagram

Actors: Passenger, Operator, System (ElevatorController).

Use Case Summary: Passengers call elevators from floors or select destination floors inside elevators. The ElevatorController assigns requests to appropriate elevators (standing, approaching, or least-loaded). Operators can place elevators into maintenance and query system status. The System executes movement, door operations, and handles alarms.

graph TD
  subgraph ElevatorSystem
    UC_Call(Call Elevator)
    UC_Select(Select Destination)
    UC_Maintenance(Set Maintenance)
    UC_Query(Query Status)
    UC_Alarm(Trigger Alarm)
  end
  Passenger --> UC_Call
  Passenger --> UC_Select
  Operator --> UC_Maintenance
  Operator --> UC_Query
  System --> UC_Alarm

3. Class Diagram

Core classes and responsibilities:

  • Elevator: stateful unit (id, currentFloor, direction, state, requests queue, doors, capacity, passengers). Methods: moveOneFloor(), addRequest(floor), openDoor(), closeDoor(), addPassenger(), removePassenger(), enterMaintenance().
  • ElevatorController (Bank): manages elevators, accepts floor calls, selects elevator with scheduling heuristic (standing on floor, approaching, least-loaded), reassigns requests from failed/maintenance elevators.
  • FloorRequest / Request: encapsulates source floor, destination floor (optional), timestamp, and priority.
  • Passenger: id, destinationFloor; interacts with Elevator to board/unboard.
  • Door: open(), close(), isObstructed(); integrated with Elevator for safety.
  • ElevatorState (enum): MAINTENANCE, STAND, UP, DOWN.
  • SchedulerPolicy (interface/strategy): encapsulates selection logic for which elevator to assign a request.
classDiagram
  class Elevator { +int id +int currentFloor +Direction direction +ElevatorState state +List~int~ requests +List~Passenger~ passengers +moveOneFloor() +addRequest(int) +openDoor() +closeDoor() }
  class ElevatorController { +List~Elevator~ elevators +submitRequest(Request) +assignElevator() +getStatus() }
  class Request { +int sourceFloor +Optional~int~ destFloor +long timestamp }
  class Passenger { +int id +int destinationFloor }
  class Door { +open() +close() +isObstructed(): boolean }
  enum ElevatorState { MAINTENANCE; STAND; UP; DOWN }
  ElevatorController "1" -- "*" Elevator : manages
  Elevator "1" -- "*" Passenger : carries
  Elevator "1" -- "*" Request : accepts

4. Activity Diagrams

Activity: Passenger call -> Assignment -> Pickup

graph TD
  P1[Passenger presses call button] --> P2[Controller receives request]
  P2 --> P3[Controller selects elevator via policy]
  P3 --> P4[Elevator updates requests queue]
  P4 --> P5[Elevator moves to floor and opens door]
  P5 --> P6[Passenger boards -> select destination]
  P6 --> P7[Elevator continues serving requests]

Activity: Alarm Handling

graph TD
  A1[Alarm triggered] --> A2[Elevator stops immediate]
  A2 --> A3[If on floor -> open doors]
  A3 --> A4[Clear request list & notify Controller]
  A4 --> A5[Controller redispatches pending requests to other elevators]

5. High-Level Code Implementation

Java (skeleton)

public enum ElevatorState { MAINTENANCE, STAND, UP, DOWN }

public class Door { public void open(); public void close(); public boolean isObstructed(); }

public class Passenger { int id; int destinationFloor; }

public class Request { int sourceFloor; Integer destFloor; long timestamp; }

public class Elevator {
  int id; int currentFloor; ElevatorState state; java.util.List<Integer> requests; java.util.List<Passenger> passengers; Door door;
  public void moveOneFloor() {}
  public void addRequest(int floor) {}
  public void openDoor() {}
  public void closeDoor() {}
}

public class ElevatorController {
  java.util.List<Elevator> elevators;
  public void submitRequest(Request r) {}
  public void assignElevator() {}
  public String getStatus() { return ""; }
}

Python (skeleton, type-hinted)

from enum import Enum
from typing import List, Optional

class ElevatorState(Enum):
    MAINTENANCE = 1
    STAND = 2
    UP = 3
    DOWN = 4

class Door:
    def open(self) -> None: pass
    def close(self) -> None: pass
    def is_obstructed(self) -> bool: return False

class Passenger:
    def __init__(self, id: int, destination_floor: int) -> None:
        self.id = id
        self.destination_floor = destination_floor

class Request:
    def __init__(self, source_floor: int, dest_floor: Optional[int] = None) -> None:
        self.source_floor = source_floor
        self.dest_floor = dest_floor

class Elevator:
    def __init__(self, id: int, min_floor: int, max_floor: int) -> None:
        self.id = id
        self.current_floor = min_floor
        self.state = ElevatorState.STAND
        self.requests: List[int] = []
        self.passengers: List[Passenger] = []
        self.door = Door()
    def move_one_floor(self) -> None: pass
    def add_request(self, floor: int) -> None: pass
    def open_door(self) -> None: pass
    def close_door(self) -> None: pass

class ElevatorController:
    def __init__(self) -> None:
        self.elevators: List[Elevator] = []
    def submit_request(self, r: Request) -> None: pass
    def assign_elevator(self) -> None: pass

Comments