public final class ParkingLot { // Singleton manager private static final ParkingLot INSTANCE = new ParkingLot(); public static ParkingLot get() { return INSTANCE; } private final List<ParkingFloor> floors = new CopyOnWriteArrayList<>(); private SpotAllocationStrategy allocator = new NearestFirstAllocation(); private PricingStrategy pricing = new HourlyPricing(); private ParkingLot() {} public Ticket park(Vehicle v) { ParkingSpot spot = allocator.allocate(floors, v) .orElseThrow(() -> new IllegalStateException("Lot full for " + v.plate)); // allocate() returns a spot it already atomically occupied return new Ticket(UUID.randomUUID().toString(), spot, v, Instant.now()); } public double unpark(Ticket t, Instant exit) { double fee = pricing.price(t, exit); t.spot().release(); return fee; }}
6. Design patterns used
Strategy — SpotAllocationStrategy and PricingStrategy let allocation (nearest-first, by-floor, EV-aware) and pricing (hourly, flat, weekend) vary independently without editing ParkingLot.
Singleton — one ParkingLot manager per deployment.
Factory — a VehicleFactory / SpotFactory (omitted for brevity) centralizes creation by type, so new types are added in one place.
Observer — displays/waiting apps subscribe to spot-availability changes (sketched in follow-ups).
7. Trade-offs and alternatives
Thread-safety granularity. Per-spot synchronized tryOccupy is fine-grained and avoids a global lock; the allocator must occupy as it finds (not "find then occupy", which races). Alternative: a per-floor lock or a concurrent free-spot queue per type for O(1) allocation.
Allocation as a queue vs scan. Scanning floors is simple and fine for small lots; for huge lots keep a Queue<ParkingSpot> per SpotType so allocate is O(1).
Pricing precision. Returning double is interview-acceptable; production money should be BigDecimal / minor-unit long.
8. Common follow-up questions
Add EV charging spots with charge tracking — new SpotType + a charging-aware allocation strategy.
Monthly subscriptions / reservations — a Reservation entity and a reserved-spot pool.
Multiple entry/exit gates — gates are stateless clients of ParkingLot; concurrency is handled at the spot level.
Distributed deployment — move the free-spot pool into Redis; the Singleton becomes a service (links to the HLD module).