Command Palette

Search for a command to run...

PHASE 4Intermediate ~14 min· topic 1 of 18Level 1

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.

0/18 · 0%

Key ideas

  1. 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.

  2. 02

    Entities: ParkingSpot (spotNumber, type, floor, isFree), Floor(spots), Vehicle, Ticket, EntryGate, ExitGate, Payment.

  3. 03

    Spot types: car, bike, truck, EV — model as enum + per-type size constants.

  4. 04

    ParkingLot aggregates floors; assigning a spot = find free spot by type → occupy → issue ticket (thread-safe).

  5. 05

    Pricing: hourly rate varies by spot + vehicle type + weekends → PricePolicy strategy.

  6. 06

    Gate shared state: two cars can't take the same spot — synchronize the spot-finder over the shared collection.

  7. 07

    Extensibility: new vehicle type, new pricing rule, new payment method each map to one seam.

  8. 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

ParkingLot.javajava

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 unit

Explain without notes

01

Walk the flow of two cars entering simultaneously and prove no double-booking is possible.

02

What changes when parking becomes free for the first 30 minutes?

Practice

01

Add an EV charging spot with a usage-based surcharge (decorator or new policy).

02

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.

Back to phase