Logic Behind The Code

Parking lot LLD

A visual low-level design walkthrough for floors, slots, vehicles, allocation, vacancy, and concurrency-safe slot updates.

2Sample floors
3Vehicle types
CASConcurrent slot update

Design Intent

What your code is doing

Factory Pattern

VehicleFactory creates the correct vehicle object from VehicleType, keeping client creation logic clean.

Strategy-Like Floor Design

ParkDesign allows different floor layouts or parking-space generation strategies.

Thread-Safe Allocation

ConcurrentHashMap.replace acts like compare-and-set, so two threads cannot claim the same slot at the same time.

Design Principles

How SOLID appears in this design

01

Single Responsibility

Vehicle owns vehicle data, Slot owns slot state, and ParkingManager coordinates floors and actions.

02

Open/Closed

New vehicle types can be added by extending Vehicle and updating the factory/type configuration.

03

Encapsulation

Slot occupancy is changed through parkAt and vacate, rather than allowing random external mutation.

04

Concurrency Safety

Parking a slot checks the current value and replaces it atomically only if the expected old value still exists.

Request Flow

How one parking request moves through the classes

Vehicle Creation

Factory creates vehicle from type

The client passes type and plate. The factory returns the correct concrete vehicle.

Vehicle vehicle = VehicleFactory.createVehicle(VehicleType.CAR, "KA-01");
Interview answer
Factory centralizes object creation and avoids scattered switch logic in the client.

Parking Flow

Manager finds floor and updates slot

The manager checks the floor, reads the selected slot, verifies it is free, then atomically assigns the plate.

boolean parked = manager.parkAt(1, 4, vehicle.getPlate());
Runtime path
ClientVehicleFactoryParkingManagerSlotVehicleInfo

Deep Design Walkthrough

Why each major code decision exists

VehicleType Enum

Define supported vehicle categories in one place

VehicleType acts like a contract between UI, factory, slot configuration, and pricing logic. The parking lot has different slot groups for CAR, BUS, and BIKE, so keeping these values as an enum avoids typo-based bugs from raw strings.

enum VehicleType {
    CAR, BUS, BIKE
}
Problem it solves
Without an enum, one part of the code might use "Bike" and another might use "BIKE". That breaks matching. Enum keeps slot allocation type-safe.
Design principle: it supports Open/Closed partially. A new vehicle type is added in one controlled model instead of spreading magic strings everywhere.

Abstract Vehicle

Use abstraction because all vehicles share state

Your commented code considered an interface first. The final design uses an abstract class because every concrete vehicle needs common data: plate number and vehicle type. This avoids repeating the same fields and getters in Car, Bus, and Bike.

abstract class Vehicle {
    private final String plate;
    private final VehicleType type;

    protected Vehicle(String plate, VehicleType type) {
        this.plate = plate;
        this.type = type;
    }
}
Why not only interface?
Interface is good for behavior contracts. Here we also need shared state. Abstract class removes duplication while still allowing concrete vehicle types.
Design principle: DRY plus inheritance for common state. Each child only declares what makes it different: its type.

VehicleInfo

Represent slot capability and occupancy separately

VehicleInfo stores the slot type and the currently parked vehicle plate. When value == null, the slot is free. When value contains a plate, it is occupied.

class VehicleInfo {
    VehicleType type;
    String value;
}
Problem it solves
A slot has two different meanings: what kind of vehicle it accepts, and whether a vehicle is currently parked. This object keeps both together.
Design principle: encapsulating related state makes allocation logic easier to reason about.

Slot Initialization

Pre-configure slot ranges by vehicle type

The Slot constructor creates a floor layout. In your code, slots 1-19 are for cars, 21-49 are for buses, and 51-99 are for bikes.

public Slot() {
    initSlot(1, 20, VehicleType.CAR);
    initSlot(21, 50, VehicleType.BUS);
    initSlot(51, 100, VehicleType.BIKE);
}
Important interview note
The loop uses i < en, so initSlot(1, 20) creates slots 1 through 19, not 1 through 20.
Design improvement: make this configurable later with floor config instead of hardcoding ranges.

ConcurrentHashMap

Protect slot state during parallel parking

A real parking lot can have many entry points. Two vehicles may try to park in the same slot at nearly the same time. ConcurrentHashMap gives safe concurrent access to slot state.

private ConcurrentHashMap<Integer, VehicleInfo> slotArray =
    new ConcurrentHashMap<>();
Problem it solves
Normal HashMap is unsafe when multiple threads read/write together. It can cause inconsistent state.
Design principle: thread safety belongs close to shared mutable data.

Atomic Allocation

Use replace(old, new) to avoid double booking

The allocation logic first reads the current slot value. It only replaces the slot if the value is still the same object. If another thread already parked there, replace fails.

if (slotArray.replace(slot, current, newInfo)) {
    allocated.add(slot);
}
Why this matters
This is compare-and-set style logic. It prevents two vehicles from occupying the same slot.
Design principle: correctness under concurrency. The check and update become one atomic operation.

Vacate Logic

Free a slot without changing its vehicle type

When a vehicle exits, the slot should become free but still remain a car/bus/bike slot. That is why vacate creates new VehicleInfo(current.type, null).

VehicleInfo newInfo = new VehicleInfo(current.type, null);
return slotArray.replace(slotNumber, current, newInfo);
Problem it solves
Vacating should remove occupancy, not erase slot configuration.
Design principle: preserve invariant. Slot type is permanent; parked plate is temporary.

ParkingManager

Separate orchestration from raw slot storage

ParkingManager keeps a map of floor number to slot layout. It exposes high-level operations like availability, park, vacate, and print across floors.

private final Map<Integer, Slot> floors = new LinkedHashMap<>();

public void addFloor(int floorNumber, Slot slot) {
    floors.put(floorNumber, slot);
}
Problem it solves
The client should not manually inspect every floor and every slot. Manager gives one clean coordinator.
Design principle: Facade-like orchestration. The client calls intent-based methods.

Code Walkthrough

Explain each important snippet like an interview answer

Vehicle Abstraction

Common data for all vehicles

The abstract class avoids duplication because every vehicle has plate and type.

abstract class Vehicle {
    private final String plate;
    private final VehicleType type;
}
Design decision
Abstract class is better than interface here because we want shared state, not only behavior.

Slot Allocation

Atomic replace prevents double booking

The slot is assigned only if the old free value is still present.

if (map.replace(slotNumber, current, newInfo)) {
    return true;
}
Concurrency note
This is compare-and-set style logic using ConcurrentHashMap.

Manager

Coordinator for floors and parking actions

The manager holds floor-to-slot mapping and exposes methods like park, vacate, and availability.

private final Map<Integer, Slot> floors = new LinkedHashMap<>();

public void addFloor(int floorNumber, Slot slot) {
    floors.put(floorNumber, slot);
}
Responsibility
Manager coordinates the use case, while Slot owns actual slot state.

Vehicle Factory

Create concrete vehicles at runtime

The caller does not need to directly instantiate Car, Bus, or Bike.

switch (type) {
    case CAR: return new Car(noPlate);
    case BUS: return new Bus(noPlate);
    case BIKE: return new Bike(noPlate);
}
Pattern
This is Factory Pattern. It keeps creation logic in one place.

Run The Code

Read it here, run it in the embedded Java compiler

Java Source

Compiler-friendly version: no package line, no scanner input, and runnable class is Main.

Embedded Compiler

Copy the Java source on the left and paste it into the compiler. It runs sample parking, availability, and vacate operations automatically.

Interview Ready

Notes For Interview

How to explain the design

This Parking Lot LLD models the system around floors, slots, vehicles, and a parking manager. Vehicles share common data through an abstract `Vehicle` class, while concrete vehicle types like `Car`, `Bus`, and `Bike` extend it. `ParkingManager` coordinates allocation and vacancy, and each floor owns slot state through `Slot`.

Flow Diagram

Vehicle enters parking lot
VehicleFactory creates vehicle
ParkingManager scans floors
Slot checks vehicle type availability
Atomic replace reserves slot
Ticket / assigned slot returned

UML Diagram: IS-A and HAS-A

Vehicle
Car
Bike
ParkingManager
Map<Floor, Slot>
Slot
Slot
ConcurrentHashMap<Integer, VehicleInfo>
VehicleFactory
Vehicle

Core classes

  • Vehicle: abstract base class for common vehicle state like plate and type.
  • Car, Bus, Bike: concrete vehicle classes with specific `VehicleType`.
  • VehicleInfo: stores slot occupancy details.
  • Slot: manages slot availability and assignment for a floor.
  • ParkingManager: coordinates floors and parking operations.
  • VehicleFactory: centralizes object creation for vehicles.

Why these patterns are used

An abstract class is used for `Vehicle` because every vehicle has shared state: plate number and vehicle type. If we used only an interface, each concrete class would repeat the same fields and getters. The abstract class gives inheritance for common data while still allowing concrete classes such as `Car`, `Bike`, and `Bus` to represent specific vehicle categories.

Factory Pattern is used for vehicle creation because object creation should be centralized. The caller can pass a `VehicleType`, and the factory decides whether to create a `Car`, `Bus`, or `Bike`. This makes the creation logic easy to extend when new vehicle types are added.

The design also uses composition heavily. `ParkingManager` has floors, a floor has slots, and a slot has occupancy information. This HAS-A structure is better than forcing inheritance between unrelated concepts.

Algorithm explanation

The parking allocation algorithm scans floors and slots to find the first compatible free slot for the requested vehicle type. Once a candidate slot is found, it tries to reserve the slot. The important detail is that reservation should be atomic so two parallel entry-gate requests cannot assign the same slot.

In the current design, `ConcurrentHashMap` and atomic replacement help protect slot state. The algorithm is essentially a first-fit allocation strategy. It is simple and predictable, but not always optimal. In a production system, this could be replaced by a strategy such as nearest-slot, lowest-floor-first, gate-aware allocation, or reserved-slot allocation.

Strong interview answer

"I would separate parking orchestration from slot state. The manager scans floors and delegates actual slot assignment to the floor's slot container. Vehicles use inheritance because they share common state, and atomic slot updates prevent two requests from booking the same slot."

Production improvements

  • Add ticket, payment, pricing strategy, entry gate, and exit gate classes.
  • Support nearest-slot or best-slot allocation strategy.
  • Persist active tickets and parking history.
  • Add reservations, electric charging slots, and disabled-accessible slots.
  • Use distributed locks if multiple entry gates run on different machines.

Weakness to mention

The demo captures slot allocation, but a full parking lot design should model tickets, gates, pricing, payment, time-based fee calculation, and persistence.