Design Parking Lot
Design a parking lot system that can handle multiple floors, vehicle types, parking spots, and ticketing with payment calculation.
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..."
"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.
"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
"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.
"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.
"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.
"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:
- 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
- ?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)
- • 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
Core Classes Summary
Enums
3Implementation
Complete implementation with all classes. Focus on the core logic - interviewers want to see clean, working code.
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
getInstance() with synchronized blockcreateSpot(SpotType type)interface PricingStrategy { double calculate(); }abstract isCompatible() in each Spot type5API 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
Add ElectricSpot extends ParkingSpot with charger field. Add ChargingState enum. Calculate charging fee separately.
Add reservedFor field in ParkingSpot. Add ReservationService to manage bookings. Check reservation before assigning.
EntryPanel and ExitPanel classes. Each calls central ParkingLot. Use synchronized methods or distributed lock for spot allocation.
ValetService handles parking/retrieval. Customer gets claim ticket. Valet has access to all spots.
LPRService scans plates at entry/exit. Maps plate to ticket. Enables ticketless parking.
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