LLD Problem - Most Asked

Design Parking Lot

Design a parking lot system that can handle multiple floors, vehicle types, parking spots, and ticketing with payment calculation.

35 min readAmazon, Google, Microsoft

0Interview Script - How to Present

First 2 minutes: Clarify requirements. Don't jump into coding!

"Before I start designing, let me clarify a few requirements..."

0-2 minRequirements Gathering

"How many floors and spots per floor? What vehicle types - just cars or also motorcycles and trucks? Is pricing hourly or flat? Multiple entry/exit points? Any special spots like handicap or reserved?"

Tip: Write down assumptions. Shows structured thinking.

2-5 minIdentify Core Objects

"Let me identify the main entities: ParkingLot, ParkingFloor, ParkingSpot, Vehicle, Ticket, Payment. I'll also need enums for VehicleType and SpotType."

Tip: Start with nouns from requirements → Classes

5-10 minClass Diagram & Relationships

"ParkingLot HAS-A list of ParkingFloors (Composition). Each Floor HAS-A list of Spots. Vehicle and Spot have an association through Ticket. I'll use inheritance for Vehicle types and Spot types."

Tip: Draw UML as you explain. Show IS-A vs HAS-A.

10-20 minCore Method Implementation

"Let me implement the key flow: parkVehicle() finds an available spot, assigns the vehicle, and returns a ticket. unparkVehicle() calculates fee and releases the spot."

Tip: Start with happy path, then handle edge cases.

20-25 minDesign Patterns

"I'm using Singleton for ParkingLot since there's only one lot. Strategy pattern for pricing if we need different strategies. Factory for creating spots."

Tip: Mention patterns naturally, don't force them.

25-30 minExtensions & Edge Cases

"For concurrent entry points, I'd use synchronized methods or locks. For EV charging, add ElectricSpot subclass. For reserved spots, add a status field."

Tip: Show you can think beyond basic requirements.

1Requirements Gathering

Before designing, clarify requirements with the interviewer:

Functional Requirements
  • Multiple floors with parking spots
  • Different spot sizes (compact, regular, large)
  • Different vehicle types (motorcycle, car, truck)
  • Issue ticket on entry
  • Calculate payment on exit
  • Display available spots per floor
Questions to Ask
  • ?How many floors/spots? (Affects data structure choice)
  • ?Is pricing hourly or flat rate? (Pricing strategy)
  • ?Multiple entry/exit points? (Concurrency concern)
  • ?Reserved/handicap spots? (Special spot types)
  • ?Electric vehicle charging? (Extension)
  • ?Payment methods? (Payment strategy)
Sample Assumptions to State:
  • • Single entry/exit point (can extend later)
  • • Hourly pricing, different rates per spot type
  • • 5 floors, ~100 spots per floor
  • • Cash/Card payment supported

2Class Design & UML

Class Diagram

ParkingLot«singleton»- floors: List<ParkingFloor>- hourlyRate: double+ parkVehicle(): Ticket1..*ParkingFloor- floorNumber: int- spots: List<ParkingSpot>+ findAvailableSpot()1..*ParkingSpot«abstract»- id: String- isFree: boolean+ isCompatible()Vehicle«abstract»- licensePlate: String- type: VehicleTypeParkingTicket- ticketId: String- entryTime: DateTime- vehicle: Vehicle+ calculateFee(): doubleCompactSpotRegularSpotLargeSpot

Core Classes Summary

Singleton
ParkingLot
- name
- floors[]
- entryPanels[]
- exitPanels[]
+ addFloor()
+ isFull()
+ getAvailableSpots()
Class
ParkingFloor
- floorNumber
- spots[]
- displayBoard
+ getAvailableSpot()
+ parkVehicle()
+ freeSpot()
Abstract
ParkingSpot
- id
- isFree
- vehicle
- spotType
+ assignVehicle()
+ removeVehicle()
+ isCompatible()
Abstract
Vehicle
- licensePlate
- vehicleType
- ticket
+ assignTicket()
Class
ParkingTicket
- ticketId
- entryTime
- exitTime
- vehicle
- spot
+ calculateFee()
Class
Payment
- amount
- timestamp
- status
- method
+ processPayment()

Enums

VehicleType
MOTORCYCLE
CAR
TRUCK
SpotType
COMPACT
REGULAR
LARGE
PaymentStatus
PENDING
COMPLETED
FAILED
TicketStatus
ACTIVE
PAID
LOST

3Implementation

Complete implementation with all classes. Focus on the core logic - interviewers want to see clean, working code.

Parking Lot System - Complete Implementation
1// ========== Enums ==========
2enum VehicleType { MOTORCYCLE, CAR, TRUCK }
3enum SpotType { COMPACT, REGULAR, LARGE }
4enum PaymentStatus { PENDING, COMPLETED, FAILED }
5
6// ========== Vehicle Classes ==========
7abstract class Vehicle {
8    private String licensePlate;
9    private VehicleType type;
10    private ParkingTicket ticket;
11    
12    public Vehicle(String plate, VehicleType type) {
13        this.licensePlate = plate;
14        this.type = type;
15    }
16    
17    public VehicleType getType() { return type; }
18    public String getLicensePlate() { return licensePlate; }
19    public void assignTicket(ParkingTicket ticket) { this.ticket = ticket; }
20    public ParkingTicket getTicket() { return ticket; }
21}
22
23class Motorcycle extends Vehicle {
24    public Motorcycle(String plate) { super(plate, VehicleType.MOTORCYCLE); }
25}
26
27class Car extends Vehicle {
28    public Car(String plate) { super(plate, VehicleType.CAR); }
29}
30
31class Truck extends Vehicle {
32    public Truck(String plate) { super(plate, VehicleType.TRUCK); }
33}
34
35// ========== Parking Spot ==========
36abstract class ParkingSpot {
37    private String id;
38    private boolean isFree = true;
39    private Vehicle vehicle;
40    private SpotType spotType;
41    
42    public ParkingSpot(String id, SpotType type) {
43        this.id = id;
44        this.spotType = type;
45    }
46    
47    public boolean isFree() { return isFree; }
48    public SpotType getType() { return spotType; }
49    public String getId() { return id; }
50    
51    public boolean canFitVehicle(Vehicle v) {
52        return isFree && isCompatible(v.getType());
53    }
54    
55    // Template method - subclasses define compatibility
56    protected abstract boolean isCompatible(VehicleType type);
57    
58    public synchronized void assignVehicle(Vehicle v) {
59        if (!isFree) throw new IllegalStateException("Spot occupied");
60        this.vehicle = v;
61        this.isFree = false;
62    }
63    
64    public synchronized Vehicle removeVehicle() {
65        Vehicle v = this.vehicle;
66        this.vehicle = null;
67        this.isFree = true;
68        return v;
69    }
70}
71
72class CompactSpot extends ParkingSpot {
73    public CompactSpot(String id) { super(id, SpotType.COMPACT); }
74    
75    @Override
76    protected boolean isCompatible(VehicleType type) {
77        return type == VehicleType.MOTORCYCLE || type == VehicleType.CAR;
78    }
79}
80
81class RegularSpot extends ParkingSpot {
82    public RegularSpot(String id) { super(id, SpotType.REGULAR); }
83    
84    @Override
85    protected boolean isCompatible(VehicleType type) {
86        return type != VehicleType.TRUCK;
87    }
88}
89
90class LargeSpot extends ParkingSpot {
91    public LargeSpot(String id) { super(id, SpotType.LARGE); }
92    
93    @Override
94    protected boolean isCompatible(VehicleType type) {
95        return true; // Can fit any vehicle
96    }
97}
98
99// ========== Parking Ticket ==========
100class ParkingTicket {
101    private String ticketId;
102    private LocalDateTime entryTime;
103    private LocalDateTime exitTime;
104    private Vehicle vehicle;
105    private ParkingSpot spot;
106    private Map<SpotType, Double> hourlyRates;
107    
108    public ParkingTicket(Vehicle v, ParkingSpot spot, Map<SpotType, Double> rates) {
109        this.ticketId = UUID.randomUUID().toString().substring(0, 8);
110        this.entryTime = LocalDateTime.now();
111        this.vehicle = v;
112        this.spot = spot;
113        this.hourlyRates = rates;
114    }
115    
116    public String getTicketId() { return ticketId; }
117    public ParkingSpot getSpot() { return spot; }
118    
119    public double calculateFee() {
120        exitTime = LocalDateTime.now();
121        long hours = Duration.between(entryTime, exitTime).toHours();
122        if (hours == 0) hours = 1; // Minimum 1 hour
123        double rate = hourlyRates.getOrDefault(spot.getType(), 5.0);
124        return hours * rate;
125    }
126}
127
128// ========== Parking Floor ==========
129class ParkingFloor {
130    private int floorNumber;
131    private List<ParkingSpot> spots;
132    
133    public ParkingFloor(int number, int compact, int regular, int large) {
134        this.floorNumber = number;
135        this.spots = new ArrayList<>();
136        
137        for (int i = 0; i < compact; i++)
138            spots.add(new CompactSpot("F" + number + "-C" + i));
139        for (int i = 0; i < regular; i++)
140            spots.add(new RegularSpot("F" + number + "-R" + i));
141        for (int i = 0; i < large; i++)
142            spots.add(new LargeSpot("F" + number + "-L" + i));
143    }
144    
145    public synchronized ParkingSpot findAvailableSpot(Vehicle v) {
146        // Try to find smallest compatible spot first (space optimization)
147        for (ParkingSpot spot : spots) {
148            if (spot.canFitVehicle(v)) {
149                return spot;
150            }
151        }
152        return null;
153    }
154    
155    public int getAvailableCount() {
156        return (int) spots.stream().filter(ParkingSpot::isFree).count();
157    }
158    
159    public int getAvailableCount(SpotType type) {
160        return (int) spots.stream()
161            .filter(s -> s.isFree() && s.getType() == type)
162            .count();
163    }
164}
165
166// ========== Parking Lot (Singleton) ==========
167class ParkingLot {
168    private static volatile ParkingLot instance;
169    private List<ParkingFloor> floors;
170    private Map<SpotType, Double> hourlyRates;
171    private Map<String, ParkingTicket> activeTickets;
172    
173    private ParkingLot() {
174        floors = new ArrayList<>();
175        hourlyRates = new HashMap<>();
176        hourlyRates.put(SpotType.COMPACT, 3.0);
177        hourlyRates.put(SpotType.REGULAR, 5.0);
178        hourlyRates.put(SpotType.LARGE, 8.0);
179        activeTickets = new ConcurrentHashMap<>();
180    }
181    
182    // Thread-safe singleton with double-checked locking
183    public static ParkingLot getInstance() {
184        if (instance == null) {
185            synchronized (ParkingLot.class) {
186                if (instance == null) {
187                    instance = new ParkingLot();
188                }
189            }
190        }
191        return instance;
192    }
193    
194    public void addFloor(ParkingFloor floor) {
195        floors.add(floor);
196    }
197    
198    public synchronized ParkingTicket parkVehicle(Vehicle vehicle) {
199        for (ParkingFloor floor : floors) {
200            ParkingSpot spot = floor.findAvailableSpot(vehicle);
201            if (spot != null) {
202                spot.assignVehicle(vehicle);
203                ParkingTicket ticket = new ParkingTicket(vehicle, spot, hourlyRates);
204                vehicle.assignTicket(ticket);
205                activeTickets.put(ticket.getTicketId(), ticket);
206                System.out.println("Parked " + vehicle.getLicensePlate() + 
207                    " at " + spot.getId());
208                return ticket;
209            }
210        }
211        System.out.println("Parking lot full!");
212        return null;
213    }
214    
215    public double unparkVehicle(ParkingTicket ticket) {
216        if (ticket == null) {
217            throw new IllegalArgumentException("Invalid ticket");
218        }
219        double fee = ticket.calculateFee();
220        ParkingSpot spot = ticket.getSpot();
221        spot.removeVehicle();
222        activeTickets.remove(ticket.getTicketId());
223        System.out.println("Fee: $" + fee);
224        return fee;
225    }
226    
227    public void displayAvailability() {
228        System.out.println("=== Parking Availability ===");
229        for (int i = 0; i < floors.size(); i++) {
230            ParkingFloor floor = floors.get(i);
231            System.out.println("Floor " + i + ": " + 
232                floor.getAvailableCount() + " spots available");
233        }
234    }
235    
236    public boolean isFull() {
237        return floors.stream().allMatch(f -> f.getAvailableCount() == 0);
238    }
239}
240
241// ========== Main / Demo ==========
242public class ParkingLotDemo {
243    public static void main(String[] args) {
244        ParkingLot lot = ParkingLot.getInstance();
245        lot.addFloor(new ParkingFloor(1, 5, 10, 2));
246        lot.addFloor(new ParkingFloor(2, 5, 10, 2));
247        
248        Car car1 = new Car("ABC-123");
249        Car car2 = new Car("XYZ-789");
250        Motorcycle bike = new Motorcycle("BIKE-001");
251        
252        ParkingTicket t1 = lot.parkVehicle(car1);
253        ParkingTicket t2 = lot.parkVehicle(car2);
254        ParkingTicket t3 = lot.parkVehicle(bike);
255        
256        lot.displayAvailability();
257        
258        // Simulate time passing...
259        double fee = lot.unparkVehicle(t1);
260        System.out.println("Car 1 paid: $" + fee);
261    }
262}

4Design Patterns Used

Singleton
Used in: ParkingLot
Only one parking lot instance should exist. Use double-checked locking for thread safety.
getInstance() with synchronized block
Factory Method
Used in: SpotFactory
Create appropriate spot types (Compact/Regular/Large) based on configuration.
createSpot(SpotType type)
Strategy
Used in: PricingStrategy
Different pricing strategies (hourly, daily, weekend rates). Easy to switch at runtime.
interface PricingStrategy { double calculate(); }
Template Method
Used in: ParkingSpot.isCompatible()
Base class defines algorithm structure, subclasses implement specific steps.
abstract isCompatible() in each Spot type

5API Design (Bonus)

If asked about REST APIs or system integration:

// Entry - Get ticket
POST /api/parking/entry
Body: { "licensePlate": "ABC-123", "vehicleType": "CAR" }
Response: { "ticketId": "T-001", "spotId": "F1-R5", "entryTime": "..." }

// Check availability
GET /api/parking/availability
Response: { "floors": [{ "floorNum": 1, "compact": 5, "regular": 8, "large": 2 }] }

// Exit - Calculate fee
POST /api/parking/exit
Body: { "ticketId": "T-001" }
Response: { "fee": 15.00, "duration": "3 hours" }

// Process payment
POST /api/parking/payment
Body: { "ticketId": "T-001", "amount": 15.00, "method": "CARD" }
Response: { "status": "COMPLETED", "receiptId": "R-001" }

6Extensions to Discuss

Electric Vehicle ChargingMedium

Add ElectricSpot extends ParkingSpot with charger field. Add ChargingState enum. Calculate charging fee separately.

Reserved/VIP SpotsMedium

Add reservedFor field in ParkingSpot. Add ReservationService to manage bookings. Check reservation before assigning.

Multiple Entry/Exit PointsHigh

EntryPanel and ExitPanel classes. Each calls central ParkingLot. Use synchronized methods or distributed lock for spot allocation.

Valet ParkingMedium

ValetService handles parking/retrieval. Customer gets claim ticket. Valet has access to all spots.

License Plate RecognitionLow

LPRService scans plates at entry/exit. Maps plate to ticket. Enables ticketless parking.

Dynamic PricingMedium

Strategy pattern: PeakHourPricing, WeekendPricing, EventPricing. PricingEngine selects strategy based on context.

7Interview Follow-up Questions

Interview Follow-up Questions

Common follow-up questions interviewers ask

8Key Takeaways

1Start with requirements clarification - ask about capacity, vehicle types, pricing model.
2Identify core entities first: ParkingLot, Floor, Spot, Vehicle, Ticket.
3Use inheritance for Vehicle hierarchy and Spot hierarchy (IS-A relationships).
4Apply Singleton for ParkingLot, Strategy for pricing, Factory for spot creation.
5Make spot allocation thread-safe with synchronized methods or locks.
6Discuss extensibility: EV charging, valet, reserved spots, dynamic pricing.
7Draw UML diagram as you explain relationships.