DEV Community

Cover image for Parking Lot System Design (LLD in Action)
AnkitDevCode
AnkitDevCode

Posted on • Edited on

Parking Lot System Design (LLD in Action)

What is Low-Level Design (LLD)?

Low-Level Design (LLD) is the process of translating High-Level Design (HLD) into concrete implementation (like class diagrams, interfaces, object relationships, and design patterns) that can be directly implemented in code.

Core Components of LLD

You can find a more in-depth information of this concept in my earlier post Object-oriented programming System(OOPs)

  • Classes and Objects
  • Interfaces and Abstractions
  • Relationships Between Classes

Key Considerations for LLD

  • Scope & Requirements
  • Clear Data Models
  • Core Design Principles (SOLID & Beyond)
  • System Robustness & Scalability
  • Design Patterns & Reusability

Common Pitfalls to Avoid

  • God classes that handle too many responsibilities.
  • Inconsistent Data Models
  • Overuse of inheritance, leading to fragile hierarchies.
  • Ignoring non-functional requirements until it’s too late.
  • Overengineering: Don’t use complex patterns unless justified—keep it lean.

Parking Lot System

Problem Statement

Design a parking lot management system for a multi-story parking garage that can handle different types of vehicles, dynamic pricing, real-time availability tracking.

Focus: Low-Level Design (LLD), Object-Oriented Programming, System Architecture

Essential Questions to Ask:

  • What types of vehicles should the system support?
  • How many floors will the parking lot have?
  • What are the different parking spot sizes?
  • How should pricing work - hourly, flat rate, or both?
  • Do we need real-time availability tracking?
  • Should the system handle reservations?
  • What payment methods are supported?
  • Do we need administrative features?

❌ Red Flags - Poor Requirements Gathering

  • Jumping straight to code without understanding requirements
  • Making assumptions without asking questions
  • Not clarifying scope - building too much or too little
  • Ignoring edge cases early in the discussion

How to approach the problem statement ?

When approaching system design, there are two common strategies:

Top-down approach → Start with the big picture. Break the larger problem into smaller, manageable parts, and then continue refining each part step by step.

  • Recommended during the analysis and design phase.
  • Starts with the big picture of the system.
  • Breaks the system into smaller, manageable sub-systems step by step.
  • Focus is on defining responsibilities and interactions at higher levels before going into details.
  • Useful for requirements gathering and system architecture design.

Bottom-up approach → Start small. Focus first on solving the smaller, individual problems, and then integrate them together to form the complete solution.

  • Recommended during the implementation phase.
  • Uses structure diagrams (e.g., class diagrams) to represent system components.
  • Focuses on breaking the problem statement into smaller components without deep details initially.
  • Behavior diagrams (e.g., sequence diagrams) are used alongside to show how the system functions overall.
  • Helps in understanding both the structure and the functionality of the system in its early stages.

Good Approach - Identify Core Components

+---------------------+       1..* +---------------------+       1..* +---------------------+
|      ParkingLot     |<>----------|    ParkingFloor     |<>----------|    ParkingSpot      |
+---------------------+            +---------------------+            +---------------------+
| - id: String        |            | - floorNumber: int  |            | - spotId: String    |
| - levels: List      |            | - spots: Map        |            | - isOccupied: bool  |
| - gates: Map        |            | - freeSpots: Map    |            | - vehicle: Vehicle  |
| - tickets: Map      |            +---------------------+            | - type: SpotType    |
+---------------------+            | + findSpot(vType)   |            +---------------------+
| + parkVehicle()     |            | + parkVehicle(v)    |                  |
| + unparkVehicle()   |            | + unparkVehicle(t)  |                  | 1
| + processPayment()  |            | + getFreeSpots()    |                  |
+---------------------+            | + notifyObserver()  |                  |
                                   +---------------------+                  |
                                                                            | 1..*
+-----------------+     +-----------------+     +-----------------+         |
|      Vehicle    |     |   ParkingTicket |     |      Payment    |         |
+-----------------+     +-----------------+     +-----------------+         |
| - license: String |   | - ticketId: int |     | - amount: double|         |
| - type: VehicleType | | - entryTime: Date |   | - method: String|         |
+-----------------+     | - spot: ParkingSpot|  | - status: String|         |
| + getType()     |     +-----------------+     +-----------------+         |
+-----------------+     | + getDuration() |                                 |
                        +-----------------+                                 |
                                                                            |
                                                                            |
                                                                            |
+------------------+     +-----------------------+                          |
|   PaymentProcessor|<-----|  DefaultPaymentProcessor|                      |
+------------------+     +-----------------------+                          |
| + process() |                                                             |
+------------------+                                                        |
                                                                            |
+------------------+     +-----------------------+                          |
|   PricingStrategy|<-----|  HourlyPricingStrategy|                         |
+------------------+     +-----------------------+                          |
| + calculateFee() |                                                        |
+------------------+                                                        |
                                                                            |
+------------------+                                                        |
|  ParkingObserver |<------------------------------------------------------+
+------------------+
| + update()       |
+------------------+ 

Enter fullscreen mode Exit fullscreen mode

Parking Lot LLD – Component Summary

1. ParkingLot

The central manager of the entire parking system.

Responsibilities:

  • Keeps track of multiple floors.
  • Handles parking and unparking.
  • Interacts with Payment and PricingStrategy.

Key Methods:

parkVehicle(vehicle): Finds an available spot and issues a ParkingTicket
unparkVehicle(ticket): Frees the spot and processes payment
processPayment(ticket, paymentMethod): Calculates fee and completes payment

2. ParkingFloor

Manages spots on a single floor.

Responsibilities:

  • Keeps track of all parking spots on the floor.
  • Notifies observers when a spot becomes occupied or free.

Key Methods:

notifyObserver(): Updates DisplayBoard or other systems
getAvailableSpot(vehicleType): Returns a free spot suitable for the vehicle

3. ParkingSpot

Represents a single parking slot.

Attributes:

spotNumber, spotType (COMPACT, LARGE, ELECTRIC, HANDICAPPED), isOccupied, vehicle

Methods:

assignVehicle(vehicle)
removeVehicle()
isOccupied()

4. ParkingTicket

Tracks a vehicle’s parking session.

Attributes:

ticketId, vehicle, spot, entryTime, exitTime

Methods:

getDuration(): Time elapsed since entry

5. Payment

Handles payment processing.

Attributes:

amount, method (CASH, CARD), status (PENDING, COMPLETED)

Methods:

process(): Completes the payment

6. PricingStrategy

Defines how fees are calculated.

calculateFee(duration): returns fee

Example Implementation:

HourlyPricingStrategy, FlatRatePricingStrategy

7. ParkingObserver

Used to update displays or other systems when a spot changes.

update(): Called when spot becomes free/occupied

Example: DisplayBoard implements ParkingObserver to show available spots.


UML Diagram Analysis - Parking Lot System

The UML class diagram illustrates a comprehensive parking lot management system that demonstrates multiple design patterns and object-oriented principles. Let me break down each section:

Please find full diagram on kroki

Class Structure:

Vehicle (Abstract)
├── Motorcycle
├── Car  
├── Truck
└── ElectricCar
Enter fullscreen mode Exit fullscreen mode

UML Relationships Explained:

Inheritance (Generalization) - Solid Line with Empty Triangle:

Vehicle <|-- Motorcycle
Vehicle <|-- Car
Vehicle <|-- Truck
Vehicle <|-- ElectricCar
Enter fullscreen mode Exit fullscreen mode

Composition and Aggregation Relationships

Strong Composition (Filled Diamond):

  • ParkingLot "has many" ParkingFloor
  • ParkingFloor "has many" ParkingSpot
  • ParkingSpot "has one" Vehicle
ParkingLot "1" *-- "many" ParkingFloor
ParkingFloor "1" *-- "many" ParkingSpot
Enter fullscreen mode Exit fullscreen mode

Meaning:

  • Parking floors cannot exist without the parking lot
  • Parking spots cannot exist without their floor
  • Lifecycle dependency: Child objects destroyed when parent is destroyed

Weak Aggregation (Empty Diamond):

ParkingLot "1" o-- "many" ParkingObserver
Enter fullscreen mode Exit fullscreen mode

Meaning:

  • Observers can exist independently of the parking lot
  • Loose coupling: Observers can be shared across multiple parking lots

Simple Association (Solid Line):

ParkingSpot --> Vehicle : parkedVehicle
ParkingTicket --> ParkingSpot
ParkingTicket --> Payment
Enter fullscreen mode Exit fullscreen mode

Meaning:

  • Objects reference each other but are independent
  • Temporary relationships: Vehicle can move to different spots

Design Pattern Implementations

Strategy Pattern:

PricingStrategy (Interface)
├── HourlyPricingStrategy
└── FlatRatePricingStrategy

Enter fullscreen mode Exit fullscreen mode

UML Notation

PricingStrategy <|.. HourlyPricingStrategy
PricingStrategy <|.. FlatRatePricingStrategy
Enter fullscreen mode Exit fullscreen mode

  • Interface implementation relationship
  • Enables runtime strategy switching
  • Open/Closed Principle: Add new strategies without modifying existing code

Observer Pattern:

Relationship: ParkingLot "1" o-- "many" ParkingObserver
Enter fullscreen mode Exit fullscreen mode
  • Loose coupling: ParkingLot doesn't know concrete observer types
  • Event-driven: Automatic notifications on state changes

Factory Pattern:

  • Factory creates Vehicle instances but doesn't store references
  • Encapsulates creation logic: Hides vehicle instantiation complexity

Singleton Pattern:

  • Single instance guarantee: Only one parking lot can exist
  • Global access point: Available throughout the application

Builder Pattern:

Purpose: Constructs complex ParkingLot objects step by step

  • Fluent interface: Method chaining for easy configuration
  • Complex construction: Handles multi-floor setup with different spot types

Key Relationships Analysis

1. Vehicle ↔ ParkingSpot Interaction

  • Bidirectional relationship: Each knows about the other when parked
  • Validation logic: Vehicle determines if it can fit in spot

2. ParkingLot as Central Coordinator

  • Hub pattern: Central point for all parking operations
  • Dependency injection: Strategy and observers injected at runtime

Interface vs Abstract Class Decisions

Interfaces Used:

  • PricingStrategy: Behavior-only contract (no shared data)
  • ParkingObserver: Event handling contract (no shared implementation)

Abstract Classes Used:

  • Vehicle: Shared data (licensePlate, color) + abstract behavior

Decision Criteria:

Interface when: Pure behavior contract, no shared data
Abstract class when: Shared data + some abstract behaviors


Encapsulation and Access Modifiers

Field Visibility Patterns:

Private Fields (-):

  • All internal state in ParkingSpot, Payment, ParkingTicket
  • Information hiding: Implementation details not exposed

Protected Fields (#):

  • Vehicle class fields accessible to subclasses
  • Inheritance support: Subclasses can access parent data

Public Methods (+):

  • All service methods and getters
  • Clear API: Well-defined public interface

Thread Safety Considerations

ReentrantLock Usage:

  • ParkingSpot has individual locks for fine-grained control
  • ParkingFloor has floor-level locks for coordination
  • ParkingLot has system-wide locks for complex operations

Thread-Safe Collections:

  • Maps shown as ConcurrentHashMap implementations
  • Concurrent access: Multiple threads can safely access shared data

🧩 LLD Implementation

What You’ll Find in the Repository - ParkingLot implementation

  • All core components, including:
  • Vehicle and its subclasses (e.g., Car, Bike, Truck)
  • ParkingSpot, ParkingFloor
  • ParkingTicket
  • PricingStrategy with a sample HourlyPricingStrategy
  • Payment
  • PaymentProcessor
  • ParkingLot as the central controller
  • Basic Observer pattern implementation with ParkingObserver and DisplayBoard
  • Simple and clean code, designed to be easy to understand and extend for beginners.
  • README documentation that explains how to set up, run the project, and test the functionality.

📌 Note
This repository is a work in progress. The current design and implementation may have some limitations or areas that can be improved. I will continue to update and optimize the design over time to make it more robust and extensible.

For now, consider this as a good starting point to understand the Parking Lot LLD in Java. It demonstrates the core concepts and relationships clearly, while leaving room for enhancements and refinements.


Summary: Good vs Bad Approaches

✅ What Makes This Solution Strong:

  • Clear Separation of Concerns: Each class has a single responsibility
  • Proper Abstraction: Abstract classes and interfaces used appropriately
  • Thread Safety: Comprehensive concurrency handling
  • Design Patterns: Multiple patterns used correctly (Strategy, Factory, Singleton, Observer)
  • Extensibility: Easy to add new features without breaking existing code
  • Error Handling: Graceful handling of edge cases
  • Type Safety: Enums prevent invalid states and types

🚩 Common Red Flags to Avoid:

  • God Class Anti-pattern: Single class doing everything
  • Primitive Obsession: Using strings/ints instead of proper types
  • No Thread Safety: Ignoring concurrent access issues
  • Tight Coupling: Classes knowing too much about each other
  • No Error Handling: Not handling edge cases
  • Hard-coded Values: No flexibility for configuration
  • Missing Abstractions: Not using inheritance where appropriate
  • Poor Naming: Unclear or misleading class/method names

References & Credits

AI tools were used to assist in research and writing but final content was reviewed and verified by the author.

Top comments (0)