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.

Illustration of Object-Oriented Parking Lot design in Java
Real-World Analogy: Labeled Mail Slots

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 Vehicle class. Specific vehicle types (like Car) inherit from it, defining their own default VehicleType.
  • Encapsulation: The ParkingSpot class manages its own state, exposing a park(Vehicle v) method that evaluates safety constraints internally:
    if (isFree && v.type == type).
  • Delegation: The ParkingLot class 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:

  1. Step 1 (Setup):
    • Create ParkingSpot spot1(type = CAR).
    • Initialize ParkingLot with List.of(spot1).
  2. Step 2 (Park Car):
    • A Car object with license "MH12-1234" arrives. We call lot.parkVehicle(car).
    • The loop scans spots. It invokes spot1.park(car).
    • spot1 checks constraints: Is it free? Yes. Does vehicle type CAR match spot type CAR? Yes.
    • The spot is marked as occupied (isFree = false, vehicle = car). Returns true.
  3. Step 3 (Park Another Car):
    • A second Car arrives. We call lot.parkVehicle(car2).
    • Scan spots: spot1.park(car2) is called.
    • Constraint check fails because spot1.isFree is false. Returns false.
  4. Step 4 (Unpark Car):
    • The first car leaves. We call lot.unparkVehicle(car).
    • The loop searches spots for the one holding car. It finds spot1 and invokes spot1.leave().
    • spot1 clears its reference (vehicle = null) and sets isFree = true.

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.