Introduction & Problem Explanation
Designing a Parking Lot is a classic Object-Oriented Design (OOD) interview question. It assesses your ability to break down a physical, real-world system into logical software abstractions using fundamental OOP principles: Inheritance, Encapsulation, and Polymorphism.
A parking lot contains various parking spots of different sizes, and accommodates vehicles of matching sizes. To build a robust model, we must design:
- Vehicle Types: Support multiple classes of vehicles, such as Bikes, Cars, and Trucks.
- ParkingSpot Constraints: Spots are typed to match vehicle classes. A vehicle can only park in a spot of the same type.
- Parking Lot Management: A controller class that automates allocating available spots for incoming vehicles and vacating them upon departure.
Imagine you run a mailroom that receives packages of three different sizes: letters (bikes), boxes (cars),
and large crates (trucks). To store them, you have shelves with compartments of corresponding sizes: small
slots, medium slots, and large shelves. When a package arrives, you look at its size and search for the
first empty compartment that matches that size. You cannot place a large crate on a letter slot, nor do you
want to waste a large shelf on a single letter. In software, the packages are Vehicle objects,
the compartments are ParkingSpot objects, and the mailroom manager who assigns packages to
spots is the ParkingLot coordinator.
The Algorithmic Approach
Polymorphic Class Hierarchies
We leverage object-oriented principles to build a clean model:
- Polymorphism: We define a base abstract
Vehicleclass. Specific vehicle types (likeCar) inherit from it, defining their own defaultVehicleType. - Encapsulation: The
ParkingSpotclass manages its own state, exposing apark(Vehicle v)method that evaluates safety constraints internally:if (isFree && v.type == type). - Delegation: The
ParkingLotclass holds a collection of spots. When parking a vehicle, it loops through the spots and delegates the parking operation to each spot until one accepts the vehicle.
Step-by-Step Execution Walkthrough
Let's trace a parking lot containing a single CAR parking spot:
- Step 1 (Setup):
- Create
ParkingSpot spot1(type = CAR). - Initialize
ParkingLotwithList.of(spot1).
- Create
- Step 2 (Park Car):
- A
Carobject with license"MH12-1234"arrives. We calllot.parkVehicle(car). - The loop scans spots. It invokes
spot1.park(car). spot1checks constraints: Is it free? Yes. Does vehicle typeCARmatch spot typeCAR? Yes.- The spot is marked as occupied (
isFree = false,vehicle = car). Returnstrue.
- A
- Step 3 (Park Another Car):
- A second
Cararrives. We calllot.parkVehicle(car2). - Scan spots:
spot1.park(car2)is called. - Constraint check fails because
spot1.isFreeis false. Returnsfalse.
- A second
- Step 4 (Unpark Car):
- The first car leaves. We call
lot.unparkVehicle(car). - The loop searches spots for the one holding
car. It findsspot1and invokesspot1.leave(). spot1clears its reference (vehicle = null) and setsisFree = true.
- The first car leaves. We call
Key Code Snippets & Explanations
Here is why the main logic in the solution is important:
public abstract static class Vehicle: Enforces inheritance. By declaring this abstract, we ensure no generic "Vehicle" can be instantiated directly; it must be a concrete type like Car or Bike.if (isFree && v.type == type): Enforces type-safety. Prevents a Truck from parking in a Motorcycle spot.s.park(v): The delegation step. The lot itself doesn't modify the spot's attributes directly; it requests the spot to execute the action, respecting encapsulation.
Java Implementation Code
Below is the complete, self-contained Java source code that solves this problem. It also includes a
main method that traces the execution with console outputs.
package io.practise.dsa;
import java.util.*;
public class DesignParkingLot {
// Enum representing supported vehicle/spot sizes
public enum VehicleType {
BIKE,
CAR,
TRUCK
}
// Abstract base class representing a generic vehicle
public abstract static class Vehicle {
VehicleType type;
String license;
public Vehicle(VehicleType type, String license) {
this.type = type;
this.license = license;
}
}
// Concrete Car class inheriting from Vehicle
public static class Car extends Vehicle {
public Car(String license) {
super(VehicleType.CAR, license);
}
}
// Class representing a single parking spot
public static class ParkingSpot {
private final VehicleType type;
private boolean isFree;
private Vehicle vehicle;
public ParkingSpot(VehicleType type) {
this.type = type;
this.isFree = true;
}
// Park the vehicle if spot is free and type matches
public boolean park(Vehicle v) {
if (isFree && v.type == this.type) {
this.vehicle = v;
this.isFree = false;
return true;
}
return false;
}
// Vacate the spot
public void leave() {
this.vehicle = null;
this.isFree = true;
}
public boolean isFree() {
return isFree;
}
public Vehicle getVehicle() {
return vehicle;
}
}
// Controller class managing multiple spots
public static class ParkingLot {
private final List<ParkingSpot> spots;
public ParkingLot(List<ParkingSpot> spots) {
this.spots = spots;
}
// Scan and park in the first available slot
public boolean parkVehicle(Vehicle v) {
for (ParkingSpot s : spots) {
if (s.park(v)) {
return true;
}
}
return false;
}
// Scan and vacate the spot holding the vehicle
public void unparkVehicle(Vehicle v) {
for (ParkingSpot s : spots) {
if (s.getVehicle() == v) {
s.leave();
break;
}
}
}
}
public static void main(String[] args) {
System.out.println("--- Design Parking Lot Demonstration ---");
ParkingSpot spot1 = new ParkingSpot(VehicleType.CAR);
ParkingLot lot = new ParkingLot(List.of(spot1));
Car car = new Car("MH12-1234");
System.out.println("Attempting to park Car [MH12-1234]...");
boolean park1 = lot.parkVehicle(car);
System.out.println("Parking Success: " + park1 + " (Expected: true)");
System.out.println("\nAttempting to park same Car again (no empty spots)...");
boolean park2 = lot.parkVehicle(car);
System.out.println("Parking Success: " + park2 + " (Expected: false)");
System.out.println("\nCar vacating spot...");
lot.unparkVehicle(car);
System.out.println("Spot status isFree: " + spot1.isFree() + " (Expected: true)");
}
}
Conclusion
Designing a Parking Lot highlights the elegance of object-oriented modeling. By distributing logic into separate components (Vehicle subclasses, ParkingSpot verifiers, and ParkingLot coordinators), we build an extensible system capable of growing to support new vehicle classes effortlessly.