All Posts

LLD for Elevator System: Designing a Smart Lift

How do you design an elevator that serves multiple floors efficiently? We break down the Low-Level Design (LLD) using the State and Strategy patterns.

Abstract AlgorithmsAbstract Algorithms
ยทยท5 min read
Share
Share on X / Twitter
Share on LinkedIn
Copy link

TLDR: An elevator system must manage multiple cars, floors, and concurrent requests using a Dispatch Algorithm to minimize wait time. We use the State Pattern for elevator movement and the Strategy Pattern for dispatching, with the SCAN sweep algorithm at the core.


๐Ÿ“– The Hotel Lift Problem: What We Are Designing

A hotel has 10 floors and 3 elevators. At any moment:

  • External requests come in: "Someone on Floor 5 pressed UP."
  • Internal requests come in: "Someone inside Lift 1 pressed Floor 10."
  • The system must decide which car to dispatch, and which order to serve floors.

This is a classic State Machine + Scheduling design. The key insight is that a lift behaves differently based on its current state โ€” and we should model that explicitly.


๐Ÿ”ข Domain Model: The Core Entities

classDiagram
    class Building {
        +List~ParkingFloor~ floors
        +List~ElevatorCar~ cars
        +Dispatcher dispatcher
    }
    class ElevatorCar {
        +int id
        +int currentFloor
        +Direction direction
        +ElevatorState state
        +TreeSet~Integer~ stops
        +handleRequest(Request r)
        +move()
    }
    class Dispatcher {
        +ElevatorCar assign(Request r)
    }
    class Request {
        +int sourceFloor
        +Direction direction
    }

    Building --> ElevatorCar
    Building --> Dispatcher
    Dispatcher --> ElevatorCar
  • ElevatorCar: Holds a sorted set of destination floors. Has a state (IDLE, MOVING_UP, MOVING_DOWN).
  • Dispatcher: The brain. Assigns incoming Request to the best car.
  • Request: Either ExternalRequest (floor + direction pressed outside) or InternalRequest (destination floor pressed inside the car).

โš™๏ธ The SCAN Algorithm: Why Not First-Come-First-Served?

FCFS is naive: if requests come from floors 10, 1, 10, the lift zigzags 1โ†’10โ†’1โ†’10 (4 ร— 9 = 36 floors traveled).

The SCAN Algorithm (also called Elevator Algorithm):

  1. Move in the current direction (UP) serving all stops along the way.
  2. When no more stops in that direction, reverse.

Toy example: Lift at Floor 4, moving UP. Requests: Floor 6 (UP), Floor 2 (DOWN), Floor 8 (UP).

StepLift PositionAction
1Floor 4 โ†’ 6Serve Floor 6 (on the way UP)
2Floor 6 โ†’ 8Serve Floor 8 (on the way UP)
3Floor 8 (switch)No more UP requests โ†’ reverse to DOWN
4Floor 8 โ†’ 2Serve Floor 2 on the way DOWN

Total travel: 6 floors instead of 18 (FCFS). This is why real elevators use SCAN variants.


๐Ÿง  State Pattern: Modeling Elevator Movement

An elevator's behavior depends on its current state. We make this explicit with the State pattern:

public interface ElevatorState {
    void handleRequest(ElevatorCar car, int targetFloor);
    String name();
}

public class IdleState implements ElevatorState {
    public void handleRequest(ElevatorCar car, int targetFloor) {
        car.addStop(targetFloor);
        if (targetFloor > car.getCurrentFloor()) {
            car.setState(new MovingUpState());
        } else if (targetFloor < car.getCurrentFloor()) {
            car.setState(new MovingDownState());
        }
    }
    public String name() { return "IDLE"; }
}

public class MovingUpState implements ElevatorState {
    public void handleRequest(ElevatorCar car, int targetFloor) {
        // SCAN: only add if it's above current floor
        if (targetFloor >= car.getCurrentFloor()) {
            car.addStop(targetFloor);   // serve on the way
        } else {
            car.deferRequest(targetFloor);  // serve on the next sweep
        }
    }
    public String name() { return "MOVING_UP"; }
}

The state transitions:

stateDiagram-v2
    [*] --> IDLE
    IDLE --> MOVING_UP : request above current floor
    IDLE --> MOVING_DOWN : request below current floor
    MOVING_UP --> IDLE : no more up requests
    MOVING_UP --> MOVING_DOWN : switch at top
    MOVING_DOWN --> IDLE : no more down requests
    MOVING_DOWN --> MOVING_UP : switch at bottom

โš™๏ธ Dispatcher: Strategy Pattern for Assignment Policy

The Dispatcher uses the Strategy pattern so we can swap assignment algorithms:

public interface DispatchStrategy {
    ElevatorCar select(List<ElevatorCar> cars, Request request);
}

// Nearest-Car strategy: pick the car that will reach the requester soonest
public class NearestCarStrategy implements DispatchStrategy {
    public ElevatorCar select(List<ElevatorCar> cars, Request request) {
        return cars.stream()
            .filter(c -> c.canService(request))
            .min(Comparator.comparingInt(c ->
                Math.abs(c.getCurrentFloor() - request.getSourceFloor())))
            .orElse(null);
    }
}

canService() checks the car's state:

  • IDLE โ†’ always serviceable.
  • MOVING_UP โ†’ serviceable if request is above current floor and direction matches.
  • MOVING_DOWN โ†’ serviceable if request is below current floor and direction matches.

โš–๏ธ Design Patterns Applied

PatternWhereWhy
StateElevatorCar.stateBehavior changes based on direction; avoids if (IDLE) {...} else if (MOVING_UP) {...}
StrategyDispatcherSwap between Nearest-Car, Least-Loaded, and Zone-Based dispatch without changing the Dispatcher class
CommandRequest objectsEncapsulate a floor request; supports queuing and undo (open-door requests)
ObserverFloor display panelsSubscribe to ElevatorCar.currentFloor updates to show position

๐Ÿ“Œ Summary

  • State Pattern models elevator direction changes as first-class state transitions โ€” no branching spaghetti.
  • SCAN Algorithm minimizes travel distance by sweeping in one direction before reversing.
  • Strategy Pattern in the Dispatcher keeps assignment policy pluggable.
  • Concurrent requests from multiple floors are handled via per-car TreeSet<Integer> of stops, which is auto-sorted.

๐Ÿ“ Practice Quiz

  1. Why is FCFS (First Come First Served) a poor dispatch strategy for elevators?

    • A) It requires more memory.
    • B) It causes zigzag travel that maximizes floors traveled; SCAN sweeps in one direction, minimizing waste.
    • C) FCFS can't handle requests from multiple floors.
      Answer: B
  2. In the State Pattern for elevator movement, what happens when a request arrives for a floor below the current floor while the car is MOVING_UP?

    • A) The car immediately reverses to MOVING_DOWN.
    • B) The request is deferred to the next downward sweep โ€” the car continues upward first.
    • C) The request is rejected.
      Answer: B
  3. What is the advantage of using a TreeSet<Integer> to store pending stops in an ElevatorCar?

    • A) It prevents duplicates and keeps stops automatically sorted by floor number, enabling in-order serving.
    • B) It uses less memory than a list.
    • C) It allows thread-safe concurrent modification without locks.
      Answer: A

Abstract Algorithms

Written by

Abstract Algorithms

@abstractalgorithms

More Posts

System Design Service Discovery and Health Checks: Routing Traffic to Healthy Instances

TLDR: Service discovery is how clients find the right service instance at runtime, and health checks are how systems decide whether an instance should receive traffic. Together, they turn dynamic infrastructure from guesswork into deterministic routi...

โ€ข8 min read

System Design Roadmap: A Complete Learning Path from Basics to Advanced Architecture

TLDR: This roadmap organizes every system-design-tagged post in this repository into learning groups and a recommended order. It is designed for interview prep and practical architecture thinking, from fundamentals to scaling, reliability, and implem...

โ€ข10 min read

System Design Observability, SLOs, and Incident Response: Operating Systems You Can Trust

TLDR: Observability is how you understand system behavior from telemetry, SLOs are explicit reliability targets, and incident response is the execution model when those targets are at risk. Together, they convert operational chaos into measurable, re...

โ€ข8 min read

System Design Message Queues and Event-Driven Architecture: Building Reliable Asynchronous Systems

TLDR: Message queues and event-driven architecture let services communicate asynchronously, absorb bursty traffic, and isolate failures. The core design challenge is not adding a queue. It is defining delivery semantics, retry behavior, and idempoten...

โ€ข8 min read