OOD - Elevator System
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:
- Support multiple elevators (2-10) operating independently in a building
- Handle hall calls: passengers request elevator from floor with Up/Down direction
- Handle car calls: passengers inside elevator select destination floors
- Implement scheduling algorithm to assign hall calls to appropriate elevators
- Move elevators between floors in response to requests
- Open and close doors at appropriate floors with safety checks
- Prevent door closure if obstructed (sensor detection)
- Track current floor, direction, and state for each elevator
- Enforce maximum capacity limits (reject boarding if overloaded)
- Support emergency stop with alarm activation
- Handle maintenance mode (take elevator offline)
- Display current floor and direction indicators
- Support configurable building floors (e.g., -2 to 50)
- Queue and prioritize requests (internal car calls prioritized over hall calls)
Non-Functional Requirements:
- Safety: Doors must not close on passengers; elevators must not move with doors open
- Performance: Average wait time <30 seconds during normal load
- Reliability: System must handle elevator failures gracefully (reassign requests)
- Scalability: Support buildings with 2-100 floors and 2-20 elevators
- 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:
ElevatorStateenum defines statesElevator.statetracks 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:
Schedulerinterface defines contractSCANSchedulerimplements 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:
Requestbase class with timestamp, priorityHallRequestandCarRequestsubclasses- 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