AlgoMaster Logo

Design Elevator System

Ashish

Ashish Pratap Singh

medium

An elevator system is a combination of mechanical components and software logic used in multi-story buildings to transport people or goods vertically between floors.

Elevator System

Modern elevator systems typically consist of one or more elevator cars (also called lifts), each controlled by an embedded software system that manages a range of operations, including:

  • Movement control (moving the elevator up or down)
  • Door operations (opening and closing doors safely and efficiently)
  • User request handling (both from inside and outside the elevator)
  • Scheduling logic to decide which elevator responds to which request, in what order, and in which direction

In this chapter, we will explore the low-level design of an elevator system in detail.

Lets start by clarifying the requirements:

1. Clarifying Requirements

Before starting the design, it's important to ask thoughtful questions to uncover hidden assumptions and better define the scope of the system.

Here is an example of how a conversation between the candidate and the interviewer might unfold:

After gathering the details, we can summarize the key system requirements.

1.1 Functional Requirements

  • Support multiple elevators operating within the same building
  • Handle both internal (cabin) and external (hall) floor requests
  • Assign requests to the most appropriate elevator, based on proximity and direction
  • Simulate basic elevator behaviors like moving between floors and stopping at requested floors
  • Display real-time updates for each elevator’s current floor and movement direction (UP/DOWN)

1.2 Non-Functional Requirements

  • Modularity: The system should follow object-oriented principles with clearly defined components
  • Scalability: The design should be scalable to support buildings with many elevators and floors
  • Concurrency Handling: The system must handle simultaneous floor requests without conflicts or race conditions
  • Responsiveness: The system should respond quickly to user input and reflect changes (e.g., floor updates, direction changes) in real-time
  • Maintainability: The design should be clean, modular, and easy to test, debug, or enhance

After the requirements are clear, lets identify the core entities/objects we will have in our system.

2. Identifying Core Entities

Core entities are the fundamental building blocks of our system. We identify them by analyzing key nouns (e.g., elevator, request, floor, direction, display) and actions (e.g., move, assign, display, handle) from the functional requirements. These typically translate into classes, enums, or interfaces in an object-oriented design.

Below, we break down the functional requirements and extract the relevant entities. Related requirements are grouped together when they correspond to the same conceptual entities.

1. The system should support multiple elevators in a building.

This clearly suggests the need for an Elevator entity that encapsulates the state and behavior of an individual elevator.

We also need an ElevatorSystem (or ElevatorController) entity to manage all elevators, process incoming requests, and delegate tasks to the appropriate elevator.

2. The system must handle internal and external requests.

This implies a Request entity that captures floor requests, their origin (inside or outside the elevator), and direction.

To represent direction, we define a Direction enum with values UP and DOWN.

3. Each elevator should show its current floor and direction.

This leads to the need for an ElevatorDisplay entity. Each elevator is equipped with a display component that continuously updates and shows the elevator’s current floor and movement direction.

3. Designing Classes and Relationships

3.1 Class Definitions

Enums

Enums
  • Direction: A simple enumeration (UP, DOWN, IDLE) that defines the possible movement states of an elevator. This is crucial for both request handling and state management.
  • RequestSource: Distinguishes between requests made from inside the elevator cabin (INTERNAL) and those from a building floor (EXTERNAL). This helps in prioritizing and routing requests correctly.

Data Classes

Request

A data-centric class that encapsulates all information about a user's request.

Request

It holds the targetFloor, the desired direction (for external requests), and the source. This makes passing request information throughout the system clean and simple.

Core Classes

Elevator

This is the central active object representing a single elevator car.

Elevator

It implements Runnable to operate on its own thread, manages its internal request queues (upRequests, downRequests), and maintains its current state (IdleState, MovingUpState, etc.). It also acts as the "Subject" in the Observer pattern.

ElevatorSystem

The main controller and entry point for the entire system.

ElevatorSystem

It's a Singleton that initializes and manages all Elevator instances. It acts as a Facade, providing a simple API for external and internal requests while hiding the underlying complexity of elevator selection and request dispatching.

3.2 Class Relationships

The relationships between classes are designed to promote low coupling and high cohesion.

Composition

A "has-a" relationship where the part cannot exist without the whole

  • ElevatorSystem has an ElevatorSelectionStrategy: The strategy is an essential, integral part of the system, created and owned by it.
  • ElevatorSystem has Elevators: The system creates, manages, and contains the elevators. The elevators' lifecycle is directly controlled by the ElevatorSystem and its thread pool.
  • Elevator has an ElevatorState: Every elevator has a state object that defines its current behavior. The elevator manages the lifecycle of its state objects.

Aggregation

A "has-a" relationship where the part can exist independently

  • Elevator has ElevatorObservers: The Elevator holds a list of observers (like Display), but the observers are created externally and can exist independently of the elevator.

Implementation (An "is-a" relationship)

  • IdleState, MovingUpState, and MovingDownState implement the ElevatorState interface.
  • NearestElevatorStrategy implements the ElevatorSelectionStrategy interface.
  • Display implements the ElevatorObserver interface.
  • Elevator implements the Runnable interface, making it an active object that can run on a thread.

Association

  • Elevator processes Requests: An elevator maintains collections of requests it needs to serve.
  • ElevatorSystem receives and delegates Requests to the appropriate elevator.

3.3 Key Design Patterns

Several design patterns are employed to create a robust and flexible system architecture.

Singleton Pattern

The ElevatorSystem class is implemented as a Singleton to ensure there is only one instance controlling the entire building's elevator network. This provides a single, global point of access and prevents conflicting states.

Strategy Pattern

This pattern is used to select an elevator for an external request.

  • Context: ElevatorSystem
  • Strategy Interface: ElevatorSelectionStrategy
  • Concrete Strategy: NearestElevatorStrategy
  • Benefit: The dispatching algorithm is decoupled from the main system. We can easily introduce new strategies (e.g., "LeastBusyElevatorStrategy") without modifying the ElevatorSystem.

State Pattern

This pattern manages the complex, state-dependent behavior of an elevator.

  • Context: Elevator
  • State Interface: ElevatorState
  • Concrete States: IdleState, MovingUpState, MovingDownState
  • Benefit: It cleanly encapsulates what an elevator does in a particular state. The Elevator class becomes simpler, as it just delegates actions to the current state object. This makes the code easier to understand and extend (e.g., adding a MaintenanceState).

Observer Pattern

This pattern provides a way to notify multiple objects about state changes in an elevator.

  • Subject: Elevator
  • Observer: ElevatorObserver (implemented by Display)
  • Benefit: It decouples the Elevator from its observers. We can attach any number of displays, loggers, or monitoring tools to an elevator without changing its code. The elevator simply notifies all registered observers when its floor or direction changes.

Facade Pattern

The ElevatorSystem acts as a facade. It provides a simple, unified interface (requestElevator(), selectFloor()) to the more complex underlying subsystem of elevators, states, strategies, and threads. This simplifies the interaction for the client (ElevatorSystemDemo).

3.4 Full Class Diagram

4. Implementation

4.1 Enums: Direction and RequestSource

  • Direction defines the elevator’s movement state.
  • RequestSource distinguishes between cabin (internal) and hall (external) requests—critical for prioritizing and routing.

4.2 Request

Encapsulates all the information needed to process a user's request.

4.3 Observer Pattern: Display

To provide real-time updates on the elevator's status without coupling the Elevator class to a specific display mechanism, we use the Observer pattern.

The Elevator is the "Subject" and Display is the "Observer". The Elevator notifies all its registered observers (notifyObservers()) whenever its state (floor, direction) changes. This allows us to add any number of different observers (e.g., a logging service, a graphical UI, a maintenance monitor) without modifying the Elevator class.

4.4 Elevator Selection Strategy

To make the algorithm for assigning an external request to an elevator pluggable, we use the Strategy pattern.

ElevatorSelectionStrategy interface defines a contract for any selection algorithm. By programming to this interface, the main ElevatorSystem is decoupled from the specific implementation of the selection logic. We could easily introduce new strategies (e.g., "least busy elevator," "energy-saving elevator") without changing the core system.

NearestElevatorStrategy selects the best elevator for a hall request using the nearest moving-in-same-direction or idle heuristic.

4.5 Elevator State Machine

The behavior of an elevator (how it moves, how it accepts new requests) changes drastically depending on whether it's idle, moving up, or moving down. The State pattern is a perfect fit to manage this complexity.

ElevatorState

This interface defines the operations that depend on the elevator's state. The Elevator class will delegate calls to these methods to its current state object, effectively changing its behavior by changing its state object.

Each state class encapsulates the logic for that specific state. For example, MovingUpState only concerns itself with servicing the next highest floor in its queue.

IdleState

MovingUpState

The states are responsible for managing transitions. For instance, when MovingUpState has no more up-requests, it transitions the elevator's state to IdleState (or MovingDownState if down-requests exist). This keeps the transition logic clean and localized.

MovingDownState

4.6 Elevator

The Elevator class brings together the State and Observer patterns. It runs in its own thread to simulate independent operation.

Explanation:

  • Active Object: By implementing Runnable, each Elevator instance acts as an "active object" that runs its own lifecycle in a dedicated thread.
  • Request Queues: TreeSet is used for upRequests and downRequests. This data structure is ideal because it automatically keeps the floor numbers sorted, making it trivial to find the next floor to visit (first()). For downRequests, we provide a reverse-order comparator to ensure the highest floor number is always first.
  • Delegation to State: The run() loop and addRequest() method simply delegate the real work to the current state object. The Elevator itself doesn't need to know how to move or add a request; it just knows that its current state does.
  • Thread Safety: currentFloor is an AtomicInteger to ensure visibility and atomicity of floor updates across threads. addRequest is synchronized to prevent race conditions when multiple threads (e.g., the main system thread and the elevator's own thread) try to modify the request sets.

4.7 ElevatorSystem (Singleton + Facade)

This class acts as the central coordinator and public-facing API (Facade) for the entire system.

The class is a Singleton to ensure there's only one control system. It provides a simple, clean API (requestElevator, selectFloor) that hides the internal complexity of strategies, states, and threads.

It uses a FixedThreadPool to manage the Elevator threads. This provides a bounded resource pool for the active elevator objects.

4.8 ElevatorSystemDemo

The main method demonstrates the end-to-end flow of the system, simulating user interactions and showing how the different components work together.

5. Run and Test

Languages
Loading...
Loading editor...

6. Quiz

Test Your Understanding

Q1.
Question 1 of 0