Problem 4.1 — Parking Lot
In one line
The canonical LLD problem. It exercises composition, the Factory pattern, pricing strategies, ticketing, and concurrency around spot assignment.
Key ideas
- 01
In plain words: a multi-storey car park has spots of different sizes. A car drives in, the system finds it a free spot and prints a ticket; when it drives out, it pays based on how long it stayed. The hard part is making sure two cars never get assigned the same spot at once.
- 02
Entities: ParkingSpot (spotNumber, type, floor, isFree), Floor(spots), Vehicle, Ticket, EntryGate, ExitGate, Payment.
- 03
Spot types: car, bike, truck, EV — model as enum + per-type size constants.
- 04
ParkingLot aggregates floors; assigning a spot = find free spot by type → occupy → issue ticket (thread-safe).
- 05
Pricing: hourly rate varies by spot + vehicle type + weekends → PricePolicy strategy.
- 06
Gate shared state: two cars can't take the same spot — synchronize the spot-finder over the shared collection.
- 07
Extensibility: new vehicle type, new pricing rule, new payment method each map to one seam.
- 08
Receipt/Ticket: id, plate, entryTime, exitTime, charges, gate info.
Java / Spring map
- →
ParkingService orchestrates; PricePolicy is a strategy; VehicleFactory creates by type; thread-safe spot map with ReentrantLock or ConcurrentHashMap.
Code & diagrams
A complete, runnable slice: lot, floors, spots, tickets, pricing and a thread-safe entry flow.
public enum SpotType { BIKE(1), CAR(2), TRUCK(4), EV(2);
public final int size;
SpotType(int size) { this.size = size; }
}
public class ParkingSpot {
private final String id;
private final SpotType type;
private final int floor;
private volatile boolean free = true; // visible across threads
public ParkingSpot(String id, SpotType type, int floor) {
this.id = id; this.type = type; this.floor = floor;
}
public synchronized boolean tryOccupy() { // atomic claim
if (!free) return false;
free = false;
return true;
}
public synchronized void release() { free = true; }
public String id() { return id; }
public SpotType type() { return type; }
public int floor() { return floor; }
}
public record Ticket(String id, String plate, SpotType type, long entryTime, ParkingSpot spot) {}
// --- pricing as a STRATEGY ---
public interface PricingPolicy {
double cost(Ticket t, long exitTime);
}
public class FlatPricing implements PricingPolicy {
private final double perHour;
public FlatPricing(double perHour) { this.perHour = perHour; }
public double cost(Ticket t, long exitTime) {
long mins = (exitTime - t.entryTime()) / 60_000L;
return Math.max(1, (mins + 59) / 60) * perHour * t.type().size; // ceil hours
}
}
// --- the lot ---
public class ParkingLot {
private final Map<SpotType, List<ParkingSpot>> freeByType = new ConcurrentHashMap<>();
public ParkingLot(int floors, int spotsPerFloor) {
for (SpotType st : SpotType.values())
freeByType.put(st, new CopyOnWriteArrayList<>());
for (int f = 1; f <= floors; f++)
for (int s = 1; s <= spotsPerFloor; s++)
for (SpotType st : SpotType.values())
freeByType.get(st).add(new ParkingSpot("F" + f + "-" + s, st, f));
}
// called from many entry-gate threads — thread-safe by construction
public Ticket enter(String plate, SpotType type) {
ParkingSpot spot = freeByType.get(type).stream()
.filter(ParkingSpot::tryOccupy)
.findFirst()
.orElseThrow(() -> new IllegalStateException("lot full for " + type));
return new Ticket("T" + System.nanoTime(), plate, type, System.currentTimeMillis(), spot);
}
public double exit(Ticket t, PricingPolicy pricing) {
long now = System.currentTimeMillis();
t.spot().release(); // free the spot
return pricing.cost(t, now);
}
}
// Demo: ParkingLot lot = new ParkingLot(2, 4);
// Ticket t = lot.enter("MH12AB1234", SpotType.CAR);
// double bill = lot.exit(t, new FlatPricing(50)); // ₹50/hr per size unitExplain without notes
Walk the flow of two cars entering simultaneously and prove no double-booking is possible.
What changes when parking becomes free for the first 30 minutes?
Practice
Add an EV charging spot with a usage-based surcharge (decorator or new policy).
Write the unit tests for enter/exit including the 'lot full' and 'concurrent entry' cases.
Trade-offs
- ↔
Scanning a free-spot list is O(n); a global counter is faster but can't answer per-type availability.
- ↔
CopyOnWriteArrayList is simple but memory-heavy at huge scale; a fine-grained lock map is the production answer.
Completion checklist
I can design Parking Lot on a whiteboard and defend the concurrent spot-claim in 20 minutes.