Skip to main content
Parking Lot System Design
Difficulty: 🟒 Beginner | Time: 45 minutes | Patterns: Singleton, Strategy, Factory

🎯 Problem Statement

Design a parking lot system that can:
  • Handle multiple floors and different vehicle types
  • Track available and occupied spots
  • Calculate parking fees based on duration
  • Support multiple entry/exit points
Why This Problem? Parking Lot is the #1 most asked LLD problem because it tests OOP basics, relationships, and simple design patterns without being overwhelming.

πŸ“‹ Step 1: Clarify Requirements

Interview Tip: ALWAYS ask clarifying questions before designing. Jumping straight to code is a red flag!

Questions to Ask the Interviewer

CategoryQuestionPossible Answer
VehiclesWhat types of vehicles do we support?Cars, motorcycles, trucks
SpotsAre spot sizes different per vehicle type?Yes - compact, regular, large
FloorsHow many floors? Multiple entry/exit?3 floors, 2 entries, 2 exits
PricingFixed rate or hourly? Different per vehicle?Hourly, varies by vehicle type
PaymentWhat payment methods?Cash, card, mobile
FeaturesReservations? Electric charging?Out of scope for now

Functional Requirements

  • Park vehicles of different types (car, motorcycle, truck)
  • Assign the nearest available spot to a vehicle
  • Calculate fees based on vehicle type and duration
  • Support multiple payment methods
  • Display available spots per floor

Non-Functional Requirements

  • Handle concurrent entry/exit (thread safety)
  • Fast spot lookup - O(1) for availability check
  • Scalable to multiple parking lots

🧩 Step 2: Identify Core Objects

Technique: List all nouns from requirements β†’ potential classes. List all verbs β†’ potential methods.

Vehicles

Car, Motorcycle, Truck

Parking Spots

Compact, Regular, Large

Infrastructure

ParkingLot, Floor, Entry/Exit

Entity-Responsibility Mapping

EntityResponsibilities
VehicleStore type, license plate; check if fits in spot
ParkingSpotTrack availability; assign/remove vehicles
FloorManage spots on one floor; find available spot
ParkingLotCoordinate all floors; issue tickets
ParkingTicketTrack entry time, spot; calculate fee
PaymentMethodProcess payment (Strategy pattern)

πŸ“ Step 3: Class Diagram

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     ParkingLot (Singleton)                      β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ - id: UUID                                                      β”‚
β”‚ - name: String                                                  β”‚
β”‚ - floors: List<Floor>                                           β”‚
β”‚ - activeTickets: Dict<String, ParkingTicket>                   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ + parkVehicle(vehicle): ParkingTicket                          β”‚
β”‚ + unparkVehicle(ticketId): float                               β”‚
β”‚ + getAvailability(): List<FloorInfo>                           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
                              β”‚ 1..*
                              β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                          Floor                                   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ - floorNumber: int                                              β”‚
β”‚ - spots: Dict[SpotType, List<ParkingSpot>]                     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ + findAvailableSpot(vehicle): ParkingSpot                      β”‚
β”‚ + getAvailableCount(spotType): int                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
                              β”‚ 1..*
                              β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                       ParkingSpot                                β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ - id: String                                                    β”‚
β”‚ - spotType: SpotType (COMPACT/REGULAR/LARGE)                   β”‚
β”‚ - isAvailable: bool                                             β”‚
β”‚ - vehicle: Vehicle                                              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ + canFit(vehicle): bool                                         β”‚
β”‚ + assignVehicle(vehicle): bool                                  β”‚
β”‚ + removeVehicle(): Vehicle                                      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚      <<abstract>>     β”‚       β”‚      ParkingTicket        β”‚
β”‚        Vehicle        β”‚       β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€       β”‚ - id: String              β”‚
β”‚ - licensePlate: str   β”‚       β”‚ - licensePlate: String    β”‚
β”‚ - vehicleType: enum   β”‚       β”‚ - spotId: String          β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€       β”‚ - entryTime: DateTime     β”‚
β”‚ + canFitIn(spot): boolβ”‚       β”‚ - exitTime: DateTime      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜       β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
         β–³                      β”‚ + calculateFee(): float   β”‚
    β”Œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”                 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
    β”‚    β”‚    β”‚
β”Œβ”€β”€β”€β”΄β” β”Œβ”€β”΄β”€β” β”Œβ”΄β”€β”€β”€β”€β”
β”‚Car β”‚ β”‚Motoβ”‚ β”‚Truckβ”‚
β””β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”˜

πŸ’» Step 4: Implementation

Enums and Constants

from enum import Enum
from datetime import datetime
from typing import Optional, Dict, List
from dataclasses import dataclass
import uuid

class VehicleType(Enum):
    MOTORCYCLE = 1
    CAR = 2
    TRUCK = 3

class SpotType(Enum):
    COMPACT = 1     # Fits motorcycle, car
    REGULAR = 2     # Fits motorcycle, car
    LARGE = 3       # Fits all vehicles

class TicketStatus(Enum):
    ACTIVE = 1
    PAID = 2
    LOST = 3

# Pricing per hour
HOURLY_RATES = {
    VehicleType.MOTORCYCLE: 1.0,
    VehicleType.CAR: 2.0,
    VehicleType.TRUCK: 4.0
}

Vehicle Classes

class Vehicle:
    def __init__(self, license_plate: str, vehicle_type: VehicleType):
        self.license_plate = license_plate
        self.vehicle_type = vehicle_type
    
    def can_fit_in(self, spot_type: SpotType) -> bool:
        """Check if vehicle fits in spot type"""
        raise NotImplementedError

class Motorcycle(Vehicle):
    def __init__(self, license_plate: str):
        super().__init__(license_plate, VehicleType.MOTORCYCLE)
    
    def can_fit_in(self, spot_type: SpotType) -> bool:
        return True  # Motorcycle fits anywhere

class Car(Vehicle):
    def __init__(self, license_plate: str):
        super().__init__(license_plate, VehicleType.CAR)
    
    def can_fit_in(self, spot_type: SpotType) -> bool:
        return spot_type in [SpotType.COMPACT, SpotType.REGULAR, SpotType.LARGE]

class Truck(Vehicle):
    def __init__(self, license_plate: str):
        super().__init__(license_plate, VehicleType.TRUCK)
    
    def can_fit_in(self, spot_type: SpotType) -> bool:
        return spot_type == SpotType.LARGE  # Only large spots

Parking Spot

class ParkingSpot:
    def __init__(self, spot_id: str, floor_number: int, spot_type: SpotType):
        self.id = spot_id
        self.floor_number = floor_number
        self.spot_type = spot_type
        self._vehicle: Optional[Vehicle] = None
        self._is_available = True
    
    @property
    def is_available(self) -> bool:
        return self._is_available
    
    def can_fit(self, vehicle: Vehicle) -> bool:
        """Check if vehicle can park here"""
        return self._is_available and vehicle.can_fit_in(self.spot_type)
    
    def assign_vehicle(self, vehicle: Vehicle) -> bool:
        """Park a vehicle in this spot"""
        if not self.can_fit(vehicle):
            return False
        
        self._vehicle = vehicle
        self._is_available = False
        return True
    
    def remove_vehicle(self) -> Optional[Vehicle]:
        """Remove vehicle from spot"""
        vehicle = self._vehicle
        self._vehicle = None
        self._is_available = True
        return vehicle

Parking Ticket

@dataclass
class ParkingTicket:
    id: str
    license_plate: str
    spot_id: str
    floor_number: int
    entry_time: datetime
    exit_time: Optional[datetime] = None
    status: TicketStatus = TicketStatus.ACTIVE
    
    @staticmethod
    def create(vehicle: Vehicle, spot: ParkingSpot) -> 'ParkingTicket':
        return ParkingTicket(
            id=str(uuid.uuid4()),
            license_plate=vehicle.license_plate,
            spot_id=spot.id,
            floor_number=spot.floor_number,
            entry_time=datetime.now()
        )
    
    def calculate_fee(self, vehicle_type: VehicleType) -> float:
        """Calculate parking fee based on duration"""
        if self.exit_time is None:
            self.exit_time = datetime.now()
        
        duration = self.exit_time - self.entry_time
        hours = max(1, int(duration.total_seconds() / 3600) + 1)  # Round up
        
        return hours * HOURLY_RATES[vehicle_type]

Floor

class Floor:
    def __init__(self, floor_number: int):
        self.floor_number = floor_number
        self.spots: Dict[SpotType, List[ParkingSpot]] = {
            SpotType.COMPACT: [],
            SpotType.REGULAR: [],
            SpotType.LARGE: []
        }
    
    def add_spot(self, spot: ParkingSpot):
        """Add a parking spot to this floor"""
        self.spots[spot.spot_type].append(spot)
    
    def get_available_count(self, spot_type: SpotType) -> int:
        """Get count of available spots of given type"""
        return sum(1 for spot in self.spots[spot_type] if spot.is_available)
    
    def find_available_spot(self, vehicle: Vehicle) -> Optional[ParkingSpot]:
        """Find first available spot that fits the vehicle"""
        # Try to find the smallest suitable spot
        for spot_type in SpotType:
            for spot in self.spots[spot_type]:
                if spot.can_fit(vehicle):
                    return spot
        return None
    
    def get_display_info(self) -> dict:
        """Get floor availability for display"""
        return {
            "floor": self.floor_number,
            "compact_available": self.get_available_count(SpotType.COMPACT),
            "regular_available": self.get_available_count(SpotType.REGULAR),
            "large_available": self.get_available_count(SpotType.LARGE)
        }

Parking Lot

import threading

class ParkingLot:
    _instance = None
    _lock = threading.Lock()
    
    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = super().__new__(cls)
        return cls._instance
    
    def __init__(self, name: str, num_floors: int):
        if hasattr(self, '_initialized'):
            return
        
        self.name = name
        self.floors: List[Floor] = []
        self.active_tickets: Dict[str, ParkingTicket] = {}  # ticket_id -> ticket
        self.vehicle_to_spot: Dict[str, ParkingSpot] = {}   # license -> spot
        self._lock = threading.Lock()
        self._initialized = True
        
        # Initialize floors
        for i in range(num_floors):
            self.floors.append(Floor(i + 1))
    
    def add_spots(self, floor_number: int, spot_type: SpotType, count: int):
        """Add parking spots to a floor"""
        floor = self.floors[floor_number - 1]
        for i in range(count):
            spot_id = f"F{floor_number}-{spot_type.name[0]}{i+1}"
            spot = ParkingSpot(spot_id, floor_number, spot_type)
            floor.add_spot(spot)
    
    def park_vehicle(self, vehicle: Vehicle) -> Optional[ParkingTicket]:
        """Park a vehicle and return ticket"""
        with self._lock:
            # Check if vehicle already parked
            if vehicle.license_plate in self.vehicle_to_spot:
                raise ValueError("Vehicle already parked")
            
            # Find available spot
            spot = self._find_available_spot(vehicle)
            if spot is None:
                return None  # No spot available
            
            # Assign vehicle to spot
            spot.assign_vehicle(vehicle)
            
            # Create ticket
            ticket = ParkingTicket.create(vehicle, spot)
            self.active_tickets[ticket.id] = ticket
            self.vehicle_to_spot[vehicle.license_plate] = spot
            
            return ticket
    
    def unpark_vehicle(self, ticket_id: str) -> Optional[float]:
        """Unpark vehicle and return fee"""
        with self._lock:
            ticket = self.active_tickets.get(ticket_id)
            if ticket is None or ticket.status != TicketStatus.ACTIVE:
                return None
            
            # Get spot and vehicle
            spot = self.vehicle_to_spot.get(ticket.license_plate)
            if spot is None:
                return None
            
            vehicle = spot.remove_vehicle()
            
            # Calculate fee
            fee = ticket.calculate_fee(vehicle.vehicle_type)
            ticket.status = TicketStatus.PAID
            
            # Cleanup
            del self.vehicle_to_spot[ticket.license_plate]
            
            return fee
    
    def _find_available_spot(self, vehicle: Vehicle) -> Optional[ParkingSpot]:
        """Find nearest available spot for vehicle"""
        for floor in self.floors:
            spot = floor.find_available_spot(vehicle)
            if spot:
                return spot
        return None
    
    def get_availability(self) -> List[dict]:
        """Get availability info for all floors"""
        return [floor.get_display_info() for floor in self.floors]

▢️ Step 5: Usage Example

# Create parking lot
parking_lot = ParkingLot("Downtown Parking", num_floors=3)

# Add spots to each floor
for floor in range(1, 4):
    parking_lot.add_spots(floor, SpotType.COMPACT, 10)
    parking_lot.add_spots(floor, SpotType.REGULAR, 20)
    parking_lot.add_spots(floor, SpotType.LARGE, 5)

# Park vehicles
car = Car("ABC-123")
motorcycle = Motorcycle("XYZ-789")
truck = Truck("TRK-001")

ticket1 = parking_lot.park_vehicle(car)
print(f"Car parked at: {ticket1.spot_id}")

ticket2 = parking_lot.park_vehicle(motorcycle)
print(f"Motorcycle parked at: {ticket2.spot_id}")

# Check availability
print(parking_lot.get_availability())

# Unpark and pay
import time
time.sleep(2)  # Simulate parking duration

processor = PaymentProcessor()
processor.process_payment(ticket1.id, parking_lot, CashPayment())

🎯 Key Design Decisions

In most real-world scenarios, there’s only one parking lot instance per location. Singleton ensures:
  • Consistent state across all entry/exit panels
  • No duplicate instances wasting memory
  • Single point of control for all operations
Trade-off: Makes unit testing harder. Consider dependency injection for testability.
Allows adding new payment methods (Apple Pay, UPI, Crypto) without modifying existing code:
  • Each payment method encapsulates its own logic
  • Easy to A/B test payment methods
  • Follows Open/Closed Principle
Alternative: Could use Factory pattern if payment logic is simpler.
Multiple entry/exit panels can operate simultaneously:
  • Prevents race conditions when assigning spots
  • Ensures ticket consistency
  • Handles concurrent unpark operations
Trade-off: Slight performance hit. Could use read-write locks for better read performance.
Using inheritance for vehicles provides:
  • Polymorphic behavior for canFitIn() method
  • Easy addition of new vehicle types
  • Type-safe spot assignment
Alternative: Could use composition with VehicleType enum for simpler cases.

πŸš€ Interview Extensions

Interviewer Favorite: β€œHow would you extend this design to support…”
class ReservedSpot(ParkingSpot):
    def __init__(self, spot_id, floor_number, spot_type, reserved_for: str):
        super().__init__(spot_id, floor_number, spot_type)
        self.reserved_for = reserved_for  # License plate
    
    def can_fit(self, vehicle: Vehicle) -> bool:
        return (super().can_fit(vehicle) and 
                vehicle.license_plate == self.reserved_for)
class EVChargingSpot(ParkingSpot):
    def __init__(self, spot_id, floor_number, charger_type: str):
        super().__init__(spot_id, floor_number, SpotType.REGULAR)
        self.charger_type = charger_type  # "Level2", "DC Fast"
        self.charging_rate_per_kwh = 0.15
    
    def calculate_charging_fee(self, kwh_used: float) -> float:
        return kwh_used * self.charging_rate_per_kwh
class MonthlyPass:
    def __init__(self, holder_license: str, spot_type: SpotType):
        self.holder_license = holder_license
        self.spot_type = spot_type
        self.valid_until = datetime.now() + timedelta(days=30)
    
    def is_valid(self) -> bool:
        return datetime.now() < self.valid_until

# Modify fee calculation
def calculate_fee(self, vehicle_type: VehicleType, has_pass: bool) -> float:
    if has_pass:
        return 0.0
    # ... regular calculation
def handle_lost_ticket(self, license_plate: str) -> float:
    """Charge maximum daily rate for lost tickets"""
    spot = self.vehicle_to_spot.get(license_plate)
    if not spot:
        raise ValueError("Vehicle not found in parking lot")
    
    vehicle = spot._vehicle
    max_hours = 24
    return max_hours * HOURLY_RATES[vehicle.vehicle_type]
# Add validation layers
class ParkingValidator:
    def validate_entry(self, vehicle: Vehicle) -> bool:
        # Check if vehicle is banned
        # Check if license plate is valid
        # Check if lot has capacity
        pass
    
    def validate_exit(self, ticket: ParkingTicket) -> bool:
        # Verify ticket authenticity
        # Check for payment status
        pass

πŸ“Š Complexity Analysis

OperationTime ComplexitySpace Complexity
parkVehicle()O(F Γ— S)O(1)
unparkVehicle()O(1)O(1)
getAvailability()O(F)O(F)
findAvailableSpot()O(F Γ— S)O(1)
Where F = number of floors, S = spots per floor
Optimization: Use a heap/priority queue per floor for O(log S) spot finding!

βœ… What Makes This Design Good?

PrincipleHow It’s Applied
Single ResponsibilityEach class has one job: Vehicle stores data, Spot manages allocation, Floor coordinates
Open/ClosedAdd new payment methods without changing existing code
Liskov SubstitutionCar, Motorcycle, Truck all substitute for Vehicle
Interface SegregationPaymentMethod is focused, not a fat interface
Dependency InversionParkingLot depends on abstract Vehicle, not concrete types