Parking Lot System Design

Ashutosh Kumar Singh
4 min readSep 21, 2024

--

In this design, we’ll implement a Parking Lot System using appropriate design patterns, create a class diagram, a database schema, and Java code for implementation.

Design Patterns Used

  1. Singleton Pattern:
  • Used to ensure there’s only one instance of the ParkingLot class managing the entire parking lot.

2. Factory Pattern:

  • Used to create different types of vehicles and parking spots based on their type (e.g., car, motorcycle, truck).

3. Strategy Pattern:

  • Used for choosing the right parking strategy for different vehicles (e.g., parking spots for different vehicle sizes).

4. Observer Pattern:

  • (Optional) Can be used to notify users when a spot becomes available.

Step 1: Class Diagram

+----------------------------------+
| ParkingLot | (Singleton)
+----------------------------------+
| - spots: List<ParkingSpot> |
| - vehicles: Map<Vehicle, ParkingSpot> |
| - capacity: int |
+----------------------------------+
| + getInstance(): ParkingLot |
| + parkVehicle(vehicle: Vehicle): boolean |
| + leaveVehicle(vehicle: Vehicle): void |
| + getAvailableSpots(): int |
+----------------------------------+
+----------------------------------+
| ParkingSpotFactory | (Factory)
+----------------------------------+
| + createSpot(type: String): ParkingSpot |
+----------------------------------+
+----------------------------------+
| ParkingSpot | (Abstract)
+----------------------------------+
| - id: int |
| - isAvailable: boolean |
| + parkVehicle(vehicle: Vehicle): void |
| + freeSpot(): void |
+----------------------------------+
+----------------------------+
| CarParkingSpot | (Concrete)
+----------------------------+
| + parkVehicle(vehicle: Vehicle): void |
+----------------------------+
+----------------------------+
| MotorcycleParkingSpot | (Concrete)
+----------------------------+
| + parkVehicle(vehicle: Vehicle): void |
+----------------------------+
+----------------------------+
| TruckParkingSpot | (Concrete)
+----------------------------+
| + parkVehicle(vehicle: Vehicle): void |
+----------------------------+
+----------------------------------+
| Vehicle | (Abstract)
+----------------------------------+
| - licensePlate: String |
| + getLicensePlate(): String |
+----------------------------------+
+----------------------------+
| Car | (Concrete)
+----------------------------+
+----------------------------+
| Motorcycle | (Concrete)
+----------------------------+
+----------------------------+
| Truck | (Concrete)
+----------------------------+

Step 2: Database Schema

Tables:

Step 3: Java Code Implementation

ParkingLot (Singleton)

import java.util.*;
public class ParkingLot {
private static ParkingLot instance;
private List<ParkingSpot> spots;
private Map<Vehicle, ParkingSpot> parkedVehicles;
private int capacity;
private ParkingLot(int capacity) {
this.capacity = capacity;
this.spots = new ArrayList<>();
this.parkedVehicles = new HashMap<>();
}
// Singleton instance
public static synchronized ParkingLot getInstance(int capacity) {
if (instance == null) {
instance = new ParkingLot(capacity);
}
return instance;
}
public boolean parkVehicle(Vehicle vehicle) {
for (ParkingSpot spot : spots) {
if (spot.isAvailable() && spot.canFitVehicle(vehicle)) {
spot.parkVehicle(vehicle);
parkedVehicles.put(vehicle, spot);
return true;
}
}
System.out.println("No available spot for vehicle: " + vehicle.getLicensePlate());
return false;
}
public void leaveVehicle(Vehicle vehicle) {
ParkingSpot spot = parkedVehicles.get(vehicle);
if (spot != null) {
spot.freeSpot();
parkedVehicles.remove(vehicle);
}
}
public int getAvailableSpots() {
int availableSpots = 0;
for (ParkingSpot spot : spots) {
if (spot.isAvailable()) {
availableSpots++;
}
}
return availableSpots;
}
public void addParkingSpot(ParkingSpot spot) {
if (spots.size() < capacity) {
spots.add(spot);
} else {
System.out.println("Parking lot is full");
}
}
}

ParkingSpot and Subclasses (Factory Pattern)

public abstract class ParkingSpot {
protected int id;
protected boolean isAvailable;
    public ParkingSpot(int id) {
this.id = id;
this.isAvailable = true;
}
public boolean isAvailable() {
return isAvailable;
}
public abstract boolean canFitVehicle(Vehicle vehicle); public void parkVehicle(Vehicle vehicle) {
isAvailable = false;
System.out.println("Vehicle parked in spot: " + id);
}
public void freeSpot() {
isAvailable = true;
System.out.println("Spot " + id + " is now free");
}
}
public class CarParkingSpot extends ParkingSpot {
public CarParkingSpot(int id) {
super(id);
}
@Override
public boolean canFitVehicle(Vehicle vehicle) {
return vehicle instanceof Car;
}
}
public class MotorcycleParkingSpot extends ParkingSpot {
public MotorcycleParkingSpot(int id) {
super(id);
}
@Override
public boolean canFitVehicle(Vehicle vehicle) {
return vehicle instanceof Motorcycle;
}
}
public class TruckParkingSpot extends ParkingSpot {
public TruckParkingSpot(int id) {
super(id);
}
@Override
public boolean canFitVehicle(Vehicle vehicle) {
return vehicle instanceof Truck;
}
}

Vehicle and Subclasses

public abstract class Vehicle {
protected String licensePlate;
    public Vehicle(String licensePlate) {
this.licensePlate = licensePlate;
}
public String getLicensePlate() {
return licensePlate;
}
}
public class Car extends Vehicle {
public Car(String licensePlate) {
super(licensePlate);
}
}
public class Motorcycle extends Vehicle {
public Motorcycle(String licensePlate) {
super(licensePlate);
}
}
public class Truck extends Vehicle {
public Truck(String licensePlate) {
super(licensePlate);
}
}

Factory Class to Create Parking Spots

public class ParkingSpotFactory {
public static ParkingSpot createSpot(String type, int id) {
switch (type.toLowerCase()) {
case "car":
return new CarParkingSpot(id);
case "motorcycle":
return new MotorcycleParkingSpot(id);
case "truck":
return new TruckParkingSpot(id);
default:
throw new IllegalArgumentException("Unknown parking spot type");
}
}
}

Usage Example

public class Main {
public static void main(String[] args) {
ParkingLot parkingLot = ParkingLot.getInstance(5);
        // Create parking spots
parkingLot.addParkingSpot(ParkingSpotFactory.createSpot("car", 1));
parkingLot.addParkingSpot(ParkingSpotFactory.createSpot("motorcycle", 2));
parkingLot.addParkingSpot(ParkingSpotFactory.createSpot("truck", 3));
// Create vehicles
Vehicle car = new Car("CAR-1234");
Vehicle motorcycle = new Motorcycle("MOTO-5678");
Vehicle truck = new Truck("TRUCK-9101");
// Park vehicles
parkingLot.parkVehicle(car);
parkingLot.parkVehicle(motorcycle);
parkingLot.parkVehicle(truck);
// Leave parking
parkingLot.leaveVehicle(car);
}
}

Step 4: Database Design

  1. ParkingSpot Table:
  • Columns:
  • spot_id (INT) - Primary Key.
  • type (VARCHAR) - Type of the spot (e.g., "car", "motorcycle", "truck").
  • is_available (BOOLEAN) - Whether the spot is available.

2. Vehicle Table:

  • Columns:
  • license_plate (VARCHAR) - Primary Key.
  • type (VARCHAR) - Type of the vehicle (e.g., "car", "motorcycle", "truck").

3. ParkingHistory Table:

  • Columns:
  • history_id (INT) - Primary Key.
  • vehicle_plate (VARCHAR) - Foreign Key referencing the vehicle.
  • spot_id (INT) - Foreign Key referencing the parking spot.
  • parked_time (TIMESTAMP) - The time when the vehicle was parked.
  • leave_time (TIMESTAMP) - The time when the vehicle left the parking lot.

Scaling Considerations

  • Horizontal Scaling: We can distribute parking spots and vehicles across different servers in large parking lots. The system should maintain a central registry to track which server handles which set of spots.
  • Load Balancer: A load balancer can be used to distribute parking requests (e.g., finding spots, parking vehicles) across multiple servers.
  • Replication: Database replication can be used to handle high availability and data recovery in case of failure.

--

--

Ashutosh Kumar Singh
Ashutosh Kumar Singh

Responses (2)