Hello! Welcome to the third and final LLD case study in this module.
In our previous lessons, we designed a parking lot and a library management system using our 5-step framework. You've seen how this structured approach helps break down complex problems. Today, we tackle a problem with a different set of challenges: designing an elevator control system.
Your learning outcome is to design an elevator control system, considering request scheduling and state management. While our previous designs focused more on entity relationships and data management, this problem brings algorithms and state transitions to the forefront. This is a classic interview question that tests your ability to model dynamic behavior and optimize for efficiency.
We will once again follow our trusted 5-step framework:
- Clarify Requirements & Scope
- Identify Core Entities & Use Cases
- Design Classes & Interactions
- Implement Core Logic
- Verify & Discuss Extensions
Let's begin.
Step 1: Clarify Requirements & Scope
As always, we start by turning the ambiguous prompt "Design an elevator system" into a concrete set of requirements. We need to define the system's capabilities and constraints.
Let's consult two resources that provide excellent overviews of typical requirements.
System Design Interview: Elevator System
First, the article 'System Design Interview: Elevator System' from Tech Wrench lays out the requirements for both a single elevator and the overall system, including potential optimizations. It provides a concise, text-based summary.
Please read the sections 'Requirements Of The System', 'Requirements For Each Elevator', 'Requirements For The Elevator System', and 'Optimizations'. This will give you a quick, comprehensive list of what we need to build.
Elevator System Design | Grokking the Object Oriented System Design Interview Question
Next, let's watch a segment from the 'Elevator System Design' video by Think Software. The presenter discusses the scope and various functional and non-functional requirements you should clarify with an interviewer, such as the number of elevators, optimization goals (like minimizing wait time), and the concept of operational zones.
Watch from 00:32 to 10:46. Pay attention to the breadth of requirements discussed, from the elevator's basic states (Up, Down, Idle) to system-level goals like throughput and power usage. This demonstrates the depth of inquiry expected in an interview.
From these resources, let's summarize our core requirements for a single-elevator system, which is a common starting point in interviews:
- Functional Requirements:
- A user can summon an elevator from any floor by pressing an 'Up' or 'Down' button (external request).
- Once inside, a user can select a destination floor (internal request).
- The elevator must move between floors to service these requests.
- The elevator doors should only open when the elevator is stationary at a floor.
- A display inside and outside the elevator should show its current floor and direction of travel.
- Non-Functional Requirements / Scope:
- We will design for one elevator in a building with 'N' floors.
- The system should be optimized to minimize passenger wait time.
- The system must correctly manage the elevator's state (e.g.,
MOVING_UP,IDLE). - The system must have a clear scheduling algorithm to decide the order of floors to visit.
Step 2: Identify Core Entities & Use Cases
With our requirements defined, we identify the main actors, objects (nouns), and actions (verbs).
- Actors:
Passenger(who uses the system). - Core Entities:
Elevator,Floor,Building,Button(Internal/External),Display,Door,ElevatorController(orScheduler). - Use Cases:
- Request an elevator from a floor.
- Select a destination floor.
- Move elevator.
- Stop elevator.
- Open/Close door.
The "Think Software" video you just watched also discusses identifying objects and use cases (from 11:38 to 17:59), reinforcing this list.
Step 3: Design Classes & Interactions
This is where we translate our abstract entities into a concrete class structure. For this problem, managing the elevator's state is a central challenge. A powerful way to handle this is by using the State design pattern.
The State Design Pattern for Elevator Management
The elevator's behavior changes drastically depending on its state. For example:
- If it's
MOVING_UP, a request from a floor below it is handled differently than a request from a floor above it. - If it's
IDLE, it can move in any direction to pick up the first request. - If it's moving up to pick up a passenger who wants to go down, its logic is unique.
Instead of writing a massive if-else or switch statement inside the Elevator class, the State pattern encapsulates the behavior of each state into its own class. The Elevator object (the context) holds a reference to a State object, and delegates state-specific behavior to it. When a state transition occurs, the context simply switches to a new state object.
Let's dive into a resource that explains this concept perfectly for our problem.
Simple explanation for design of an Elevator System in ...
The article 'Simple explanation for design of an Elevator System' provides a fantastic, code-centric explanation of how to apply the State pattern. It breaks down the complex logic of an elevator into manageable state classes.
Please read the sections from 'Using State design pattern' to the end of the 'Class Lift' section. Focus on: Identifying the States (Section 3): Note the five distinct states identified, including the nuanced 'MovingUpToPickFirstState'. State Class Implementation (Sections 4-8): Review the code for MovingUpState and IdleState. See how methods like getTimeToReachFloor and tick have different logic in each state class. The Lift Class (Section 9): Observe how the Lift class holds instances of all possible states and delegates calls to its current state object. This is the core of the pattern.
This state-based approach elegantly solves the "state management" part of our learning outcome.
UML Class Diagram
Now, let's visualize the overall structure, including our state-managed Elevator, a Scheduler to process requests, and other components.
Step 4: Implement Core Logic (Request Scheduling)
We've designed how the elevator manages its state. Now we must decide where it goes. This is the request scheduling algorithm, the brain of the system. This is a perfect use case for the Strategy design pattern, where we can define a family of algorithms, encapsulate each one, and make them interchangeable.
We will explore three common scheduling strategies, from the simplest to the most efficient.
🧠Elevator System Low-Level Design (LLD) – OOP, UML & Best Practices 📊
The video 'Elevator System Low-Level Design (LLD)' by codeWithAryan provides an exceptionally detailed, step-by-step dry run of three key scheduling algorithms using the same complex example. This will give you a deep understanding of their mechanics and trade-offs.
This is the most important part of the lesson. Please watch the following segments carefully: First-Come, First-Served (FCFS) (26:50 - 34:05): Understand this simple but inefficient baseline algorithm. Scan Algorithm (34:05 - 43:23): See how this improves on FCFS by moving in one direction until it hits an end, servicing all requests along the way. Note the discussion on the 'starvation' problem. Look Algorithm (43:23 - 58:30): This is the most practical and complex algorithm. Pay close attention to how it's a refinement of Scan (it only goes as far as the last request in a direction) and how the presenter's implementation carefully handles a mix of internal and external requests to decide the very next stop.
Algorithm Comparison
Let's summarize the algorithms you just saw:
| Algorithm | How it Works | Pros | Cons |
|---|---|---|---|
| FCFS | Services requests in the exact order they are received. | Simple to implement, fair (no starvation). | Highly inefficient. Lots of unnecessary up-and-down travel. |
| Scan | Moves all the way to the top floor, then all the way to the bottom, serving requests in its current direction. | More efficient than FCFS, serves multiple requests in one pass. | Wastes time traveling to the very end of the building even if there are no requests there. Can have long wait times for floors just visited. |
| Look | A smarter version of Scan. Moves in one direction only as far as the last request in that direction, then reverses. | Efficient, avoids unnecessary travel to the ends of the building. This is the basis for most real-world elevator algorithms. | More complex to implement correctly, especially when balancing internal and external requests. |
The Look algorithm, as detailed in the video, represents a very strong answer in a system design interview. It demonstrates an understanding of optimization and the subtle complexities of the problem.
Test your understanding!
Imagine an elevator using the Look algorithm is at Floor 5, moving UP. Its current destination is Floor 10 (an internal request).
While traveling from 5 to 10, it encounters two new external requests:
- A person at Floor 7 wants to go UP.
- A person at Floor 8 wants to go DOWN.
How should the elevator handle these new requests?
Show answer
The elevator should:
- Stop at Floor 7: The request at Floor 7 is in the same direction as the elevator's current travel (UP). The elevator should stop, open its doors to pick up the passenger, and then continue moving up.
- Ignore Floor 8 for now: The request at Floor 8 is for the DOWN direction. Stopping to pick up this passenger would be inefficient, as the elevator is currently on an upward trip to Floor 10. This request should be logged and serviced later, after the elevator has completed its upward journey and reversed direction.
This illustrates the core logic of the Look algorithm: service all requests in the current direction of travel before reversing.
Step 5: Verify & Discuss Extensions
To conclude, we'll verify our design with a sequence diagram and discuss how to extend it.
A. Verifying with a Sequence Diagram
This diagram shows how the objects in our system might interact to service a request.

B. Discussing Extensions
Interviewer: "Your design handles one elevator well. How would you scale it to a system with multiple elevators?"
Your Response:
"That's a great question. Scaling to multiple elevators primarily impacts the ElevatorController or Scheduler logic. When a user makes an external request (e.g., 'UP' at Floor 3), the controller must decide which of the 'N' elevators is the best one to dispatch. The 'best' elevator can be determined by a cost function. For each available elevator, the controller could calculate a score based on factors like:
- Distance: How far is the elevator from the requesting floor?
- Direction: Is the elevator already moving towards the user?
- Current Load: How many passengers are already inside?
- Internal Stops: How many stops does the elevator already have scheduled?
The elevator with the lowest 'cost' would be assigned the request. This turns the scheduling problem into an optimization problem across the entire fleet of elevators, ensuring the system as a whole remains efficient."
Conclusion
Excellent work! You've successfully designed an elevator control system, focusing on the critical aspects of state management and request scheduling.
Key Takeaways:
- State Pattern for Behavior: The State pattern is an incredibly effective tool for managing an object whose behavior changes based on its internal state, avoiding complex conditional logic.
- Strategy Pattern for Algorithms: The Strategy pattern is ideal for encapsulating and swapping out different algorithms, such as the various elevator scheduling strategies (FCFS, Scan, Look).
- Algorithms and Trade-offs: The choice of scheduling algorithm involves significant trade-offs between simplicity, efficiency, and fairness. The Look algorithm provides a robust and practical solution.
- Problem-Solving Focus: This case study shifted our focus from data modeling to managing dynamic behavior and algorithmic optimization, rounding out your LLD skills.
In our next module, we will begin exploring another fundamental pillar of system design: the SOLID principles. These five principles will provide you with a powerful mental framework for creating code that is maintainable, flexible, and robust.