Building a parking garage
Turn the entity model into working classes — the spot-allocation algorithm, duration-based pricing via Strategy, and thread-safe concurrent parking.
From model to code
The design lesson gave us entities and patterns. Now implement the core: the classes, the allocation algorithm, pricing via Strategy, and thread safety for concurrent entries/exits. (Python-flavored pseudocode; the structure is what matters.)
The enums and the spot
from enum import Enum
class VehicleType(Enum): MOTORCYCLE=1; CAR=2; TRUCK=3
class SpotType(Enum): MOTORCYCLE=1; COMPACT=2; LARGE=3
# which spot types each vehicle may use (smallest that fits, and up)
FITS = {
VehicleType.MOTORCYCLE: [SpotType.MOTORCYCLE, SpotType.COMPACT, SpotType.LARGE],
VehicleType.CAR: [SpotType.COMPACT, SpotType.LARGE],
VehicleType.TRUCK: [SpotType.LARGE],
}
class ParkingSpot:
def __init__(self, spot_id, spot_type):
self.id, self.type, self.vehicle = spot_id, spot_type, None
def is_free(self): return self.vehicle is None
Allocation: free spots per type, per level
The naive approach scans every spot — O(spots) per car. Instead, each Level keeps a set/queue of free spots bucketed by type, so finding a spot is O(1):
from collections import defaultdict
class Level:
def __init__(self, level_id, spots):
self.id = level_id
self.free = defaultdict(set) # SpotType -> {spot_id}
self.spots = {}
for s in spots:
self.spots[s.id] = s
self.free[s.type].add(s.id)
def assign(self, vehicle): # returns a spot or None
for st in FITS[vehicle.type]: # try smallest fitting type first
if self.free[st]:
spot = self.spots[self.free[st].pop()]
spot.vehicle = vehicle
return spot
return None
def release(self, spot):
spot.vehicle = None
self.free[spot.type].add(spot.id)
Trying the smallest fitting type first keeps large spots open for vehicles that truly need them — a simple, sensible allocation policy (swap in a Strategy if rules get complex).
Pricing via the Strategy pattern
Pricing is a pluggable policy so new schemes don’t touch the lot:
class PricingStrategy: # interface
def price(self, entry, exit): ...
class HourlyRate(PricingStrategy):
def __init__(self, rate): self.rate = rate
def price(self, entry, exit):
hours = ceil((exit - entry).total_seconds() / 3600)
return hours * self.rate
class TieredRate(PricingStrategy): # e.g. first hour cheap, then more
def price(self, entry, exit): ...
The exit panel just calls strategy.price(ticket.entry_time, now) — it neither
knows nor cares which scheme is active.
The top-level flow
class ParkingLot:
def __init__(self, levels, pricing):
self.levels, self.pricing = levels, pricing
self.lock = threading.Lock()
def park(self, vehicle):
with self.lock: # thread-safe assignment
for level in self.levels:
spot = level.assign(vehicle)
if spot:
return Ticket(vehicle, spot, level, now())
raise GarageFullError()
def unpark(self, ticket):
with self.lock:
ticket.level.release(ticket.spot)
fee = self.pricing.price(ticket.entry_time, now())
return fee
Thread safety: the real correctness bug
Two cars entering at once can be handed the same last spot if assignment isn’t atomic — a classic race. The fix is to guard the find-and-occupy as one critical section (the lock above), or use atomic operations on the free-spot structure. Always raise this in the interview: “parking is a read-modify-write on shared state, so I serialize the assign/release with a lock (or per-level locks to reduce contention).” Per-level locks scale better than one global lock.
Edge cases to mention
- Garage full for a vehicle’s type (large free, but a truck needs large).
- Lost ticket (charge a max/day rate).
- Vehicle occupying multiple spots (a bus needs N adjacent large spots — extend
assignto reserve a contiguous run). - Re-entrancy / double exit on the same ticket.
The takeaway
The OOD signal is: clean classes, an O(1) bucketed allocation instead of a scan, Strategy for pricing so it’s extensible, and explicitly locking the shared assignment to avoid the double-allocation race. That’s a complete, defensible implementation — and the same modeling discipline scaffolds every larger system in this chapter.