Low-Level Design · lesson 6 of 18
Beginner
4Intermediate
10- 5 Design a Parking Lot: Allocating and Freeing Spots
- 6 Design an Elevator System: Scheduling Requests
- 7 Design a Library Management System: Borrowing and Returning Books
- 8 Design an Expense-Splitting App: Balances and Minimal Settlements
- 9 Design a URL Shortener: Sequential Codes vs Real Hash Collisions
- 10 Wrapping Objects: Adapter, Facade, and Proxy in Python
- 11 The Composite Pattern in Python: Sizing a File Tree Without isinstance
- 12 The State Pattern in Python: Refactoring a Vending Machine Into State Classes
- 13 Undo and Redo in Python: the Memento and Command Patterns Compared
- 14 Chain of Responsibility in Python: Expense Approvals Without an if/elif Ladder
Advanced
4- 15 Concurrency in LLD: A Real Race Condition in the Parking Lot, Then the Fix
- 16 The Observer Pattern in Practice: A Stock Ticker Notifying Multiple Displays
- 17 Design Patterns That Are Just Language Features: Strategy, Command, Iterator
- 18 Abstract Factory and Prototype in Python: Matching Families and Cloning Objects
Also part of Low-Level Design
12- + SOLID Principles in Java from Java
- + Composition vs Inheritance in Java from Java
- + Design Patterns in Java from Java
- + Python Design Patterns from Python
- + Design Patterns in TypeScript from TypeScript
- + JavaScript Design Patterns from JavaScript
- + Design Patterns in C# from C#
- + Design Patterns in C++ from C++
- + Design Patterns in Go from Go
- + Design Patterns in Rust from Rust
- + Abstract Classes in Java from Java
- + Sealed Classes and Interfaces in Java from Java
Design an Elevator System: Scheduling Requests
Two floor requests, one elevator, and a nearest-request scheduler that has to decide which to serve first and print every floor it passes on the way.
By SkillByExample · Published
One elevator, two pending requests — the scheduler has to decide which floor to serve first.
Nearest-request scheduling
class Elevator:
def __init__(self):
self.current_floor = 0
self.requests = []
def request_floor(self, floor):
if floor == self.current_floor:
print(f"Already at floor {floor}")
return
self.requests.append(floor)
print(f"Request added: floor {floor}")
def step(self):
if not self.requests:
print("No pending requests")
return
# nearest-request scheduling: always go to the closest pending floor
target = min(self.requests, key=lambda f: abs(f - self.current_floor))
self.requests.remove(target)
direction = "up" if target > self.current_floor else "down"
for floor in range(self.current_floor, target, 1 if target > self.current_floor else -1):
next_floor = floor + (1 if target > self.current_floor else -1)
print(f"Moving {direction}: passing floor {next_floor}")
self.current_floor = target
print(f"Arrived at floor {target}, doors open")
elevator = Elevator()
elevator.request_floor(5)
elevator.request_floor(2)
elevator.step()
elevator.step()
$ python elevator.py
Request added: floor 5
Request added: floor 2
Moving up: passing floor 1
Moving up: passing floor 2
Arrived at floor 2, doors open
Moving up: passing floor 3
Moving up: passing floor 4
Moving up: passing floor 5
Arrived at floor 5, doors open
Floor 5 was requested first, but the elevator serves floor 2 first, because it’s closer to
the starting position (floor 0). The second step() call then continues on to the
still-pending floor 5 request from wherever the elevator ended up — floor 2, not floor 0.
Practice
1. Confirm requesting the current floor doesn't add a request.
class Elevator:
def __init__(self):
self.current_floor = 0
self.requests = []
def request_floor(self, floor):
if floor == self.current_floor:
print(f"Already at floor {floor}")
return
self.requests.append(floor)
print(f"Request added: floor {floor}")
e = Elevator()
e.request_floor(0)
$ python same_floor.py
Already at floor 0
No "Request added" line prints, and self.requests stays empty — the guard clause returns
before appending anything.
2. Reject requests for floors outside the building's range.
class Elevator:
MIN_FLOOR, MAX_FLOOR = 0, 10
def __init__(self):
self.current_floor = 0
self.requests = []
def request_floor(self, floor):
if not (self.MIN_FLOOR <= floor <= self.MAX_FLOOR):
print(f"Invalid floor {floor}: building has floors {self.MIN_FLOOR}-{self.MAX_FLOOR}")
return
if floor == self.current_floor:
print(f"Already at floor {floor}")
return
self.requests.append(floor)
print(f"Request added: floor {floor}")
e = Elevator()
e.request_floor(15)
e.request_floor(7)
$ python floor_bounds.py
Invalid floor 15: building has floors 0-10
Request added: floor 7
The bounds check runs before the “already here” check, since an out-of-range floor is invalid regardless of where the elevator currently is.
3. Drain the whole request queue with a loop instead of calling step() manually.
class Elevator:
def __init__(self):
self.current_floor = 0
self.requests = []
def request_floor(self, floor):
if floor == self.current_floor:
print(f"Already at floor {floor}")
return
self.requests.append(floor)
def step(self):
if not self.requests:
print("No pending requests")
return
target = min(self.requests, key=lambda f: abs(f - self.current_floor))
self.requests.remove(target)
self.current_floor = target
print(f"Arrived at floor {target}")
e = Elevator()
e.request_floor(3)
e.request_floor(1)
e.request_floor(6)
while e.requests:
e.step()
e.step()
$ python drain_queue.py
Arrived at floor 1
Arrived at floor 3
Arrived at floor 6
No pending requests
while e.requests stops the loop the instant the list is empty, then one more manual
step() call demonstrates the “nothing left to do” branch explicitly.
Next: a library system tracking which member has which book, and what “overdue” actually requires computing.