Parking Lot System Design
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
- 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
- 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.