Topic: Design a Multi-Level Parking Lot using Object-Oriented System Design (OOSD) principles
Asked in interviews at: Amazon, Apple, Google, etc.
Design Guidelines (What we consider in this exercise)
- Ignore UI, persistence, and networking. Focus on objects, responsibilities and relationships.
- Design in-memory data structures for storing and retrieving state.
- Classes, interfaces, properties and behaviors matter most.
- Use UML-style thinking to explain relationships (association, aggregation, composition).
Problem & Assumptions
We adopt the following assumptions to keep the problem tractable while still interesting:
- The parking lot has multiple levels; each level has multiple rows and spots.
- The lot must support motorcycles, cars and buses.
- Spots are of three logical sizes: Motorcycle, Compact, Large.
- A motorcycle can park in any spot.
- A car fits in a compact or large spot.
- A bus requires 5 consecutive large spots in the same row.
- To reduce complexity and fragmentation, we model spot sizes rather than separate classes per spot type.
High-level Design (OOSD concepts)
- Use an abstract
Vehicleclass with concrete subclasses:Motorcycle,Car,Bus. - Use a
ParkingSpotclass that has aVehicleSizeenum rather than separate subclasses for each spot type. Levelmanages its rows and spots.ParkingLotmanages multipleLevels.- Vehicles know how many
spotsNeededthey require and whatVehicleSizethey are. - Vehicles keep track of the
ParkingSpots in which they are parked (aggregation). - Spot ownership of a
Vehicleis composition-like — when a spot is occupied that vehicle is referenced by the spot.
UML relationships (conceptual)
- Aggregation:
LevelaggregatesParkingSpots (a level can exist without any vehicles). - Composition:
ParkingLotcomposesLevels (levels are integral parts of the lot). - Association:
Vehicle↔ParkingSpot(a vehicle is associated with the spots it occupies).
(You can draw a class diagram with ParkingLot → Level → ParkingSpot and Vehicle subclasses linking to ParkingSpot.)
Core Java Model (key classes & snippets)
1. VehicleSize enum
public enum VehicleSize {
Motorcycle, Compact, Large
}
2. Vehicle (abstract)
public abstract class Vehicle {
protected ArrayList<ParkingSpot> parkingSpots = new ArrayList<>();
protected String licensePlate;
protected int spotsNeeded;
protected VehicleSize size;
public int getSpotsNeeded() { return spotsNeeded; }
public VehicleSize getSize() { return size; }
// record that this vehicle is parked in spot s
public void parkInSpot(ParkingSpot s) {
parkingSpots.add(s);
}
// remove vehicle from all spots (implementation omitted)
public void clearSpots() { /* ... */ }
// can this vehicle fit in a particular spot? (size check only)
public abstract boolean canFitInSpot(ParkingSpot spot);
}
3. Concrete vehicles
public class Bus extends Vehicle {
public Bus() {
spotsNeeded = 5;
size = VehicleSize.Large;
}
public boolean canFitInSpot(ParkingSpot spot) {
return spot.getSpotSize() == VehicleSize.Large;
}
}
public class Car extends Vehicle {
public Car() {
spotsNeeded = 1;
size = VehicleSize.Compact;
}
public boolean canFitInSpot(ParkingSpot spot) {
return spot.getSpotSize() == VehicleSize.Compact ||
spot.getSpotSize() == VehicleSize.Large;
}
}
public class Motorcycle extends Vehicle {
public Motorcycle() {
spotsNeeded = 1;
size = VehicleSize.Motorcycle;
}
public boolean canFitInSpot(ParkingSpot spot) {
// motorcycle can park anywhere
return true;
}
}
4. ParkingSpot
public class ParkingSpot {
private Vehicle vehicle;
private VehicleSize spotSize;
private int row;
private int spotNumber;
private Level level;
public ParkingSpot(Level lvl, int r, int n, VehicleSize s) {
level = lvl; row = r; spotNumber = n; spotSize = s;
}
public boolean isAvailable() {
return vehicle == null;
}
public VehicleSize getSpotSize() { return spotSize; }
// Check if spot is big enough and available
public boolean canFitVehicle(Vehicle vehicle) {
if (!isAvailable()) return false;
// the Vehicle class handles size semantics (or we can centralize here)
if (vehicle.getSize() == VehicleSize.Motorcycle) return true;
if (vehicle.getSize() == VehicleSize.Compact)
return spotSize == VehicleSize.Compact || spotSize == VehicleSize.Large;
return spotSize == VehicleSize.Large; // for Bus individual spot check
}
// Park vehicle in this spot
public boolean park(Vehicle v) {
this.vehicle = v;
v.parkInSpot(this);
return true;
}
// Remove vehicle and notify level
public void removeVehicle() {
this.vehicle = null;
// optionally inform level of availability
}
public int getRow() { return row; }
public int getSpotNumber() { return spotNumber; }
}
Parking algorithm notes
- Small vehicles (motorcycle, car): find first available spot that
canFitVehicle()returns true and park. - Bus: must find 5 consecutive available
Largespots in the same row. TheLevelclass should implement a method to scan rows for such a block. - When you park a vehicle, mark all
ParkingSpots occupied and let theVehiclerecord the spots. - When removing a vehicle,
Vehicle.clearSpots()iterates itsparkingSpotsand callsremoveVehicle()on each spot.
Level & ParkingLot (brief)
Levelholds a 2D structure (rows × spots) and provides:parkVehicle(Vehicle v)which decides how/where to parkfindAvailableSpots(Vehicle v)which returns suitable spot(s)spotFreed()callback for bookkeeping
ParkingLotcontains multipleLevelobjects and:- iterates through levels to find available space
- provides global operations (status, total free spots, reports)
Design tradeoffs & decisions
- Single
ParkingSpotclass vs many subclasses: We favored a singleParkingSpotwithspotSizeto reduce code duplication. Use subclasses only if spots have different behaviors. - Bus parking: requires block search. This complicates allocation but avoids fragmentation if implemented carefully.
- Threading & concurrency: Real system would need locks or synchronization around spot allocation (not covered here).
- Persistence: Not part of this exercise — but in a real system you’d persist
Level/Spot/Vehiclestate to DB and restore on startup.
What to present in an interview
- Start with assumptions (what is in scope / out of scope).
- Sketch UML showing classes and relationships (ParkingLot → Level → ParkingSpot; Vehicle hierarchy).
- Explain the parking algorithm for each vehicle type and how you search for spots.
- Show key code snippets (Vehicle, ParkingSpot, Bus logic).
- Mention concurrency, persistence, and scaling concerns and how you’d address them (locks, DB transactions, cache, distributed coordination).
Summary
This design models a flexible multi-level parking lot using core OOSD principles:
- clear abstractions (
Vehicle,ParkingSpot,Level,ParkingLot) - polymorphism for vehicle behaviors
- aggregation/composition to show ownership and lifetime
- a straightforward algorithmic approach for special cases like buses.
