Home/Learn/Low Level Design/Design a Parking Lot

Design a Parking Lot

Intermediate
LLD Interview Problems
Java Source Code

Model a multi-floor parking lot that assigns spots based on vehicle size and calculates fees using a pluggable pricing strategy.

Overview

A parking lot system manages multiple floors, each containing spots of varying sizes (SMALL, MEDIUM, LARGE). Vehicles (Motorcycle, Car, Truck) require a minimum spot size. The system issues a ParkingTicket on entry, records entry time, and on exit delegates fee calculation to a PricingStrategy. The ParkingLot itself is a Singleton so all floors share one access point. A Factory creates the correct spot type or vehicle subclass from an input string. This problem tests your ability to map real-world entities to a clean class hierarchy and apply at least two behavioral patterns under interview time pressure.

Requirements Analysis

Functional: assign a spot by vehicle size, issue a ticket on entry, release a spot and calculate fee on exit, support multiple floors. Non-functional: thread-safe spot assignment (synchronized or ConcurrentHashMap), extensible pricing (new rates without changing core logic), O(1) spot lookup per floor.

Requirements
// No code here — just requirements
// Entities: ParkingLot, ParkingFloor, ParkingSpot, Vehicle, ParkingTicket, PaymentService
// Patterns : Singleton (ParkingLot), Factory (vehicle/spot), Strategy (pricing)

Core Classes & Relationships

SpotType and VehicleType are enums. Vehicle is abstract with a getType() method; Car, Truck, Motorcycle extend it. ParkingSpot holds a SpotType and a nullable Vehicle reference. ParkingLot is a Singleton that owns a list of ParkingFloors. PricingStrategy is an interface with two implementations: FlatRatePricing and HourlyPricing.

Java — enums & interfaces
public enum SpotType  { SMALL, MEDIUM, LARGE }
public enum VehicleType { MOTORCYCLE, CAR, TRUCK }

public interface PricingStrategy {
    double calculate(long entryEpoch, long exitEpoch);
}

public abstract class Vehicle {
    protected final String licensePlate;
    protected final VehicleType type;
    Vehicle(String lp, VehicleType t) { this.licensePlate = lp; this.type = t; }
    public VehicleType getType() { return type; }
}

public class Car        extends Vehicle { Car(String lp)        { super(lp, VehicleType.CAR);        } }
public class Motorcycle extends Vehicle { Motorcycle(String lp) { super(lp, VehicleType.MOTORCYCLE); } }
public class Truck      extends Vehicle { Truck(String lp)      { super(lp, VehicleType.TRUCK);      } }

Java Implementation

ParkingSpot tracks occupancy. ParkingFloor iterates its spots to find the first available one that fits the vehicle. ParkingLot.assignSpot() delegates to each floor in order and returns a ParkingTicket. PaymentService.checkout() uses the injected PricingStrategy to compute the fee and frees the spot.

Java — core classes
public class ParkingSpot {
    private final String id;
    private final SpotType type;
    private Vehicle parkedVehicle;

    public ParkingSpot(String id, SpotType type) { this.id = id; this.type = type; }
    public boolean isAvailable()  { return parkedVehicle == null; }
    public void park(Vehicle v)   { this.parkedVehicle = v; }
    public void vacate()          { this.parkedVehicle = null; }
    public SpotType getType()     { return type; }
    public String getId()         { return id; }
}

public class ParkingTicket {
    private final String ticketId;
    private final ParkingSpot spot;
    private final long entryEpoch;
    ParkingTicket(String ticketId, ParkingSpot spot) {
        this.ticketId   = ticketId;
        this.spot       = spot;
        this.entryEpoch = System.currentTimeMillis();
    }
    public ParkingSpot getSpot()    { return spot; }
    public long getEntryEpoch()     { return entryEpoch; }
}

public class ParkingFloor {
    private final List<ParkingSpot> spots;
    public ParkingFloor(List<ParkingSpot> spots) { this.spots = spots; }

    public Optional<ParkingSpot> findSpot(VehicleType vehicleType) {
        SpotType required = vehicleType == VehicleType.TRUCK ? SpotType.LARGE
                          : vehicleType == VehicleType.CAR   ? SpotType.MEDIUM
                          : SpotType.SMALL;
        return spots.stream()
            .filter(s -> s.isAvailable() && s.getType() == required)
            .findFirst();
    }
}

public class ParkingLot {                          // Singleton
    private static volatile ParkingLot instance;
    private final List<ParkingFloor> floors;
    private int ticketCounter = 0;

    private ParkingLot(List<ParkingFloor> floors) { this.floors = floors; }

    public static ParkingLot getInstance(List<ParkingFloor> floors) {
        if (instance == null) synchronized (ParkingLot.class) {
            if (instance == null) instance = new ParkingLot(floors);
        }
        return instance;
    }

    public ParkingTicket assignSpot(Vehicle vehicle) {
        for (ParkingFloor floor : floors) {
            Optional<ParkingSpot> spot = floor.findSpot(vehicle.getType());
            if (spot.isPresent()) {
                spot.get().park(vehicle);
                return new ParkingTicket("TKT-" + (++ticketCounter), spot.get());
            }
        }
        throw new IllegalStateException("No available spot for " + vehicle.getType());
    }
}

public class PaymentService {
    private final PricingStrategy strategy;
    public PaymentService(PricingStrategy strategy) { this.strategy = strategy; }

    public double checkout(ParkingTicket ticket) {
        double fee = strategy.calculate(ticket.getEntryEpoch(), System.currentTimeMillis());
        ticket.getSpot().vacate();
        return fee;
    }
}

Key Points to Remember

  • 1Use enums for SpotType and VehicleType to avoid stringly-typed comparisons and enable exhaustive switch expressions.
  • 2Singleton ParkingLot with Double-Checked Locking + volatile ensures one global instance under concurrent access.
  • 3Strategy pattern for pricing isolates fee logic — adding a WeekendSurgePricing requires zero changes to ParkingLot.
  • 4Factory method (or a static VehicleFactory) decouples callers from concrete Car/Truck/Motorcycle constructors.

Interview Questions

Sign in to ask Aria
1

How would you handle concurrent spot assignment to prevent two threads from booking the same spot?

HardAmazon
2

How would you extend the design to support reserved spots and monthly passes?

MediumMicrosoft
3

Why is a Singleton appropriate for ParkingLot but potentially problematic in a microservices architecture?

MediumUber

Ask Aria about Design a Parking Lot

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…