Introduction
Designing a parking lot system is a classic object-oriented design question at Meta that tests your ability to model real-world entities, manage state, and handle complex business rules. This question focuses on:
- Object-oriented design: Class hierarchy and relationships
- State management: Parking spot states, vehicle states
- Edge case handling: Different vehicle types, parking rules
- Efficient tracking: Occupancy management, spot finding
This guide covers the complete design of a parking lot system with proper class structure, state management, and efficient algorithms.
Table of Contents
- Problem Statement
- Requirements
- Class Design
- State Management
- Core Operations
- Edge Cases
- Efficient Tracking
- Implementation
- Summary
Problem Statement
Design a parking lot system that:
- Manages parking spots of different types (compact, regular, large)
- Handles different vehicle types (motorcycle, car, truck)
- Tracks occupancy efficiently
- Finds available spots for vehicles
- Handles parking rules (vehicle can park in larger spots)
- Calculates fees based on time
- Manages entry/exit operations
Scale Requirements:
- Support 100-10,000 parking spots
- Handle multiple vehicle types
- Fast spot finding: < 10ms
- Efficient occupancy tracking
Requirements
Functional Requirements
- Park Vehicle: Park a vehicle in appropriate spot
- Unpark Vehicle: Remove vehicle and calculate fee
- Find Spot: Find available spot for vehicle type
- Check Availability: Check if spots available
- Calculate Fee: Calculate parking fee based on time
- Track Occupancy: Monitor spot usage
Non-Functional Requirements
Performance:
- Spot finding: < 10ms
- Efficient memory usage
- Fast occupancy checks
Correctness:
- No double parking
- Correct fee calculation
- Proper state management
Class Design
Core Classes
from enum import Enum
from datetime import datetime
from typing import Optional, List
from dataclasses import dataclass
class VehicleType(Enum):
MOTORCYCLE = "motorcycle"
CAR = "car"
TRUCK = "truck"
class SpotType(Enum):
COMPACT = "compact"
REGULAR = "regular"
LARGE = "large"
class SpotStatus(Enum):
AVAILABLE = "available"
OCCUPIED = "occupied"
RESERVED = "reserved"
MAINTENANCE = "maintenance"
@dataclass
class Vehicle:
license_plate: str
vehicle_type: VehicleType
parked_at: Optional[datetime] = None
spot: Optional['ParkingSpot'] = None
def can_park_in(self, spot_type: SpotType) -> bool:
"""Check if vehicle can park in spot type."""
rules = {
VehicleType.MOTORCYCLE: [SpotType.COMPACT, SpotType.REGULAR, SpotType.LARGE],
VehicleType.CAR: [SpotType.REGULAR, SpotType.LARGE],
VehicleType.TRUCK: [SpotType.LARGE]
}
return spot_type in rules[self.vehicle_type]
class ParkingSpot:
def __init__(self, spot_id: int, spot_type: SpotType, floor: int):
self.spot_id = spot_id
self.spot_type = spot_type
self.floor = floor
self.status = SpotStatus.AVAILABLE
self.vehicle: Optional[Vehicle] = None
self.occupied_at: Optional[datetime] = None
def is_available(self) -> bool:
"""Check if spot is available."""
return self.status == SpotStatus.AVAILABLE
def park_vehicle(self, vehicle: Vehicle) -> bool:
"""Park vehicle in spot."""
if not self.is_available():
return False
if not vehicle.can_park_in(self.spot_type):
return False
self.vehicle = vehicle
self.status = SpotStatus.OCCUPIED
self.occupied_at = datetime.now()
vehicle.spot = self
vehicle.parked_at = datetime.now()
return True
def unpark_vehicle(self) -> Optional[Vehicle]:
"""Remove vehicle from spot."""
if self.status != SpotStatus.OCCUPIED:
return None
vehicle = self.vehicle
self.vehicle = None
self.status = SpotStatus.AVAILABLE
self.occupied_at = None
vehicle.spot = None
vehicle.parked_at = None
return vehicle
class Floor:
def __init__(self, floor_number: int):
self.floor_number = floor_number
self.spots: List[ParkingSpot] = []
self.spot_map: dict = {} # spot_id -> ParkingSpot
def add_spot(self, spot: ParkingSpot):
"""Add spot to floor."""
self.spots.append(spot)
self.spot_map[spot.spot_id] = spot
def get_available_spots(self, spot_type: Optional[SpotType] = None) -> List[ParkingSpot]:
"""Get available spots, optionally filtered by type."""
available = [s for s in self.spots if s.is_available()]
if spot_type:
available = [s for s in available if s.spot_type == spot_type]
return available
def get_occupancy(self) -> dict:
"""Get occupancy statistics."""
total = len(self.spots)
occupied = sum(1 for s in self.spots if s.status == SpotStatus.OCCUPIED)
return {
'total': total,
'occupied': occupied,
'available': total - occupied,
'occupancy_rate': occupied / total if total > 0 else 0
}
class ParkingLot:
def __init__(self, name: str):
self.name = name
self.floors: List[Floor] = []
self.vehicles: dict = {} # license_plate -> Vehicle
self.spot_index: dict = {} # spot_id -> ParkingSpot
self.fee_rate = {
VehicleType.MOTORCYCLE: 2.0, # per hour
VehicleType.CAR: 5.0,
VehicleType.TRUCK: 10.0
}
def add_floor(self, floor: Floor):
"""Add floor to parking lot."""
self.floors.append(floor)
# Index all spots
for spot in floor.spots:
self.spot_index[spot.spot_id] = spot
def park_vehicle(self, vehicle: Vehicle) -> Optional[ParkingSpot]:
"""Park vehicle in appropriate spot."""
if vehicle.license_plate in self.vehicles:
raise ValueError("Vehicle already parked")
# Find suitable spot
spot = self._find_spot(vehicle)
if not spot:
return None
# Park vehicle
if spot.park_vehicle(vehicle):
self.vehicles[vehicle.license_plate] = vehicle
return spot
return None
def unpark_vehicle(self, license_plate: str) -> Optional[float]:
"""Unpark vehicle and calculate fee."""
if license_plate not in self.vehicles:
return None
vehicle = self.vehicles[license_plate]
spot = vehicle.spot
if not spot:
return None
# Calculate fee
fee = self._calculate_fee(vehicle, spot)
# Unpark
spot.unpark_vehicle()
del self.vehicles[license_plate]
return fee
def _find_spot(self, vehicle: Vehicle) -> Optional[ParkingSpot]:
"""Find suitable spot for vehicle."""
# Get compatible spot types (vehicle can park in same or larger)
compatible_types = self._get_compatible_spot_types(vehicle.vehicle_type)
# Search floors in order
for floor in self.floors:
for spot_type in compatible_types:
available_spots = floor.get_available_spots(spot_type)
if available_spots:
# Return first available (or use better strategy)
return available_spots[0]
return None
def _get_compatible_spot_types(self, vehicle_type: VehicleType) -> List[SpotType]:
"""Get spot types compatible with vehicle type."""
rules = {
VehicleType.MOTORCYCLE: [SpotType.COMPACT, SpotType.REGULAR, SpotType.LARGE],
VehicleType.CAR: [SpotType.REGULAR, SpotType.LARGE],
VehicleType.TRUCK: [SpotType.LARGE]
}
return rules[vehicle_type]
def _calculate_fee(self, vehicle: Vehicle, spot: ParkingSpot) -> float:
"""Calculate parking fee."""
if not spot.occupied_at:
return 0.0
duration = datetime.now() - spot.occupied_at
hours = duration.total_seconds() / 3600
# Round up to nearest hour
hours = max(1, int(hours) + (1 if duration.total_seconds() % 3600 > 0 else 0))
rate = self.fee_rate[vehicle.vehicle_type]
return hours * rate
def get_availability(self, vehicle_type: VehicleType) -> dict:
"""Get availability for vehicle type."""
compatible_types = self._get_compatible_spot_types(vehicle_type)
total_available = 0
for floor in self.floors:
for spot_type in compatible_types:
total_available += len(floor.get_available_spots(spot_type))
return {
'vehicle_type': vehicle_type.value,
'available_spots': total_available
}
def get_occupancy_summary(self) -> dict:
"""Get overall occupancy summary."""
total_spots = 0
occupied_spots = 0
for floor in self.floors:
occupancy = floor.get_occupancy()
total_spots += occupancy['total']
occupied_spots += occupancy['occupied']
return {
'total_spots': total_spots,
'occupied_spots': occupied_spots,
'available_spots': total_spots - occupied_spots,
'occupancy_rate': occupied_spots / total_spots if total_spots > 0 else 0
}
State Management
Spot State Machine
AVAILABLE -> OCCUPIED (on park)
OCCUPIED -> AVAILABLE (on unpark)
AVAILABLE -> MAINTENANCE (admin action)
MAINTENANCE -> AVAILABLE (admin action)
AVAILABLE -> RESERVED (reservation)
RESERVED -> OCCUPIED (on park)
RESERVED -> AVAILABLE (reservation expired)
State Transitions
class ParkingSpot:
def set_maintenance(self):
"""Set spot to maintenance mode."""
if self.status == SpotStatus.OCCUPIED:
raise ValueError("Cannot set occupied spot to maintenance")
self.status = SpotStatus.MAINTENANCE
def clear_maintenance(self):
"""Clear maintenance mode."""
if self.status == SpotStatus.MAINTENANCE:
self.status = SpotStatus.AVAILABLE
def reserve(self):
"""Reserve spot."""
if self.status != SpotStatus.AVAILABLE:
raise ValueError("Can only reserve available spots")
self.status = SpotStatus.RESERVED
def cancel_reservation(self):
"""Cancel reservation."""
if self.status == SpotStatus.RESERVED:
self.status = SpotStatus.AVAILABLE
Core Operations
Parking Operation
def park_vehicle(self, license_plate: str, vehicle_type: VehicleType) -> dict:
"""Park vehicle and return result."""
# Check if already parked
if license_plate in self.vehicles:
return {
'success': False,
'message': 'Vehicle already parked'
}
# Create vehicle
vehicle = Vehicle(license_plate, vehicle_type)
# Find spot
spot = self._find_spot(vehicle)
if not spot:
return {
'success': False,
'message': 'No available spots'
}
# Park
if spot.park_vehicle(vehicle):
self.vehicles[license_plate] = vehicle
return {
'success': True,
'spot_id': spot.spot_id,
'floor': spot.floor,
'spot_type': spot.spot_type.value,
'parked_at': vehicle.parked_at.isoformat()
}
return {
'success': False,
'message': 'Failed to park vehicle'
}
Unparking Operation
def unpark_vehicle(self, license_plate: str) -> dict:
"""Unpark vehicle and return fee."""
if license_plate not in self.vehicles:
return {
'success': False,
'message': 'Vehicle not found'
}
vehicle = self.vehicles[license_plate]
fee = self._calculate_fee(vehicle, vehicle.spot)
vehicle.spot.unpark_vehicle()
del self.vehicles[license_plate]
return {
'success': True,
'fee': fee,
'duration_hours': (datetime.now() - vehicle.parked_at).total_seconds() / 3600
}
Edge Cases
Handling Different Vehicle Types
def _find_spot(self, vehicle: Vehicle) -> Optional[ParkingSpot]:
"""Find spot with preference for exact match."""
compatible_types = self._get_compatible_spot_types(vehicle.vehicle_type)
# Prefer exact match first
preferred_type = self._get_preferred_spot_type(vehicle.vehicle_type)
# Try preferred type first
for floor in self.floors:
spots = floor.get_available_spots(preferred_type)
if spots:
return spots[0]
# Try other compatible types
for floor in self.floors:
for spot_type in compatible_types:
if spot_type != preferred_type:
spots = floor.get_available_spots(spot_type)
if spots:
return spots[0]
return None
def _get_preferred_spot_type(self, vehicle_type: VehicleType) -> SpotType:
"""Get preferred spot type for vehicle."""
preferences = {
VehicleType.MOTORCYCLE: SpotType.COMPACT,
VehicleType.CAR: SpotType.REGULAR,
VehicleType.TRUCK: SpotType.LARGE
}
return preferences[vehicle_type]
Handling Multiple Floors
def _find_spot(self, vehicle: Vehicle, preferred_floor: Optional[int] = None) -> Optional[ParkingSpot]:
"""Find spot, optionally preferring specific floor."""
compatible_types = self._get_compatible_spot_types(vehicle.vehicle_type)
# If preferred floor specified, try it first
if preferred_floor is not None:
floor = next((f for f in self.floors if f.floor_number == preferred_floor), None)
if floor:
for spot_type in compatible_types:
spots = floor.get_available_spots(spot_type)
if spots:
return spots[0]
# Search all floors
for floor in self.floors:
for spot_type in compatible_types:
spots = floor.get_available_spots(spot_type)
if spots:
return spots[0]
return None
Efficient Tracking
Occupancy Tracking
class OccupancyTracker:
def __init__(self, parking_lot: ParkingLot):
self.parking_lot = parking_lot
self.spot_availability = {} # spot_type -> count
self._initialize()
def _initialize(self):
"""Initialize availability counts."""
for spot_type in SpotType:
self.spot_availability[spot_type] = 0
for floor in self.parking_lot.floors:
for spot in floor.spots:
if spot.is_available():
self.spot_availability[spot.spot_type] += 1
def update_on_park(self, spot: ParkingSpot):
"""Update counts when vehicle parks."""
self.spot_availability[spot.spot_type] -= 1
def update_on_unpark(self, spot: ParkingSpot):
"""Update counts when vehicle unparks."""
self.spot_availability[spot.spot_type] += 1
def get_availability(self, vehicle_type: VehicleType) -> int:
"""Get available spots for vehicle type."""
compatible_types = self._get_compatible_spot_types(vehicle_type)
return sum(self.spot_availability[t] for t in compatible_types)
def _get_compatible_spot_types(self, vehicle_type: VehicleType) -> List[SpotType]:
"""Get compatible spot types."""
rules = {
VehicleType.MOTORCYCLE: [SpotType.COMPACT, SpotType.REGULAR, SpotType.LARGE],
VehicleType.CAR: [SpotType.REGULAR, SpotType.LARGE],
VehicleType.TRUCK: [SpotType.LARGE]
}
return rules[vehicle_type]
Fast Spot Finding with Indexing
class ParkingLot:
def __init__(self, name: str):
# ... existing code ...
self.availability_index = {
SpotType.COMPACT: [],
SpotType.REGULAR: [],
SpotType.LARGE: []
}
self._build_index()
def _build_index(self):
"""Build index of available spots by type."""
for floor in self.floors:
for spot in floor.spots:
if spot.is_available():
self.availability_index[spot.spot_type].append(spot)
def _find_spot_fast(self, vehicle: Vehicle) -> Optional[ParkingSpot]:
"""Fast spot finding using index."""
compatible_types = self._get_compatible_spot_types(vehicle.vehicle_type)
for spot_type in compatible_types:
available = self.availability_index[spot_type]
if available:
spot = available.pop(0)
return spot
return None
def park_vehicle(self, vehicle: Vehicle) -> Optional[ParkingSpot]:
"""Park with index update."""
spot = self._find_spot_fast(vehicle)
if spot and spot.park_vehicle(vehicle):
self.vehicles[vehicle.license_plate] = vehicle
return spot
return None
def unpark_vehicle(self, license_plate: str) -> Optional[float]:
"""Unpark with index update."""
if license_plate not in self.vehicles:
return None
vehicle = self.vehicles[license_plate]
spot = vehicle.spot
fee = self._calculate_fee(vehicle, spot)
spot.unpark_vehicle()
# Update index
self.availability_index[spot.spot_type].append(spot)
del self.vehicles[license_plate]
return fee
Implementation
Complete Example
# Create parking lot
parking_lot = ParkingLot("Downtown Parking")
# Add floors
floor1 = Floor(1)
floor1.add_spot(ParkingSpot(1, SpotType.COMPACT, 1))
floor1.add_spot(ParkingSpot(2, SpotType.REGULAR, 1))
floor1.add_spot(ParkingSpot(3, SpotType.LARGE, 1))
parking_lot.add_floor(floor1)
# Park vehicle
result = parking_lot.park_vehicle("ABC123", VehicleType.CAR)
print(result) # {'success': True, 'spot_id': 2, ...}
# Check availability
availability = parking_lot.get_availability(VehicleType.CAR)
print(availability) # {'vehicle_type': 'car', 'available_spots': 1}
# Unpark vehicle
result = parking_lot.unpark_vehicle("ABC123")
print(result) # {'success': True, 'fee': 5.0, ...}
What Interviewers Look For
Object-Oriented Design Skills
- Class Design
- Proper class hierarchy and relationships
- Clear separation of concerns
- Appropriate use of inheritance/composition
- Red Flags: God classes, poor encapsulation, unclear relationships
- Modeling Real-World Entities
- Can you model real-world concepts accurately?
- Do classes represent entities correctly?
- Red Flags: Missing key entities, incorrect relationships
- State Management
- Proper state transitions
- State validation
- Red Flags: Invalid state transitions, no state validation
Problem-Solving Approach
- Edge Case Handling
- Different vehicle types
- Full parking lot scenarios
- Multiple floors
- Red Flags: Ignoring edge cases, incomplete solutions
- Efficiency Considerations
- Fast spot finding algorithms
- Efficient occupancy tracking
- Red Flags: O(n) searches, inefficient data structures
- Extensibility
- Can design handle new vehicle types?
- Easy to add new features?
- Red Flags: Hard-coded logic, not extensible
Code Quality
- Clean Code
- Readable, maintainable code
- Proper naming conventions
- Red Flags: Unclear code, poor naming
- Error Handling
- Proper validation
- Meaningful error messages
- Red Flags: No validation, unclear errors
Interview Focus
- OOP Principles
- SOLID principles application
- Design patterns usage
- Key: Show strong OOP fundamentals
- Practical Design
- Real-world constraints consideration
- Scalability thinking
- Key: Balance correctness with efficiency
Summary
Key Takeaways
- Class Hierarchy: Vehicle, ParkingSpot, Floor, ParkingLot
- State Management: Spot states (available, occupied, maintenance)
- Vehicle Types: Motorcycle, Car, Truck with different spot requirements
- Efficient Tracking: Indexing for fast spot finding
- Edge Cases: Different vehicle types, multiple floors, maintenance
Common Interview Questions
- What classes would you create?
- Vehicle, ParkingSpot, Floor, ParkingLot
- Enums for VehicleType, SpotType, SpotStatus
- How to handle different vehicle types?
- Compatibility rules (vehicle can park in same or larger spots)
- Preferred spot type matching
- Fallback to larger spots
- How would you track occupancy efficiently?
- Maintain availability index by spot type
- Update index on park/unpark
- O(1) availability checks
Design Principles
- Encapsulation: Each class manages its own state
- Separation of Concerns: Clear responsibilities
- Extensibility: Easy to add new vehicle/spot types
- Efficiency: Fast spot finding with indexing
- Correctness: Proper state management
Understanding parking lot design is crucial for Meta interviews focusing on:
- Object-oriented design
- State management
- Class hierarchy
- Edge case handling
- Efficient algorithms