Design A Parking Lot System

Β·

4 min read

πŸ“š High-Level Overview

A Parking Lot System is a software solution to manage vehicle parking in an organized way. It should handle:

  • Vehicle entry and exit.

  • Tracking available and occupied spots.

  • Calculating parking fees.

  • Supporting different vehicle types (e.g., Car, Bike, Truck).

  • Multiple parking floors (if applicable).

We will design the system considering scalability, maintainability, and using Object-Oriented Principles and appropriate Design Patterns.


πŸ› οΈ Key Requirements

Functional Requirements

  • Allow vehicles to enter and exit the parking lot.

  • Different types of vehicles may occupy different parking spots.

  • Maintain real-time status of parking spot availability.

  • Calculate parking fees based on time and vehicle type.

Non-Functional Requirements

  • Scalable: Should support multiple parking floors.

  • Efficient: Quick lookups for spot availability.

  • Maintainable: Clear separation of concerns.


🧠 Key Design Components

  1. Parking Lot: Manages all parking floors and spots.

  2. Parking Floor: Represents each floor and its spots.

  3. Parking Spot: Represents an individual parking spot.

  4. Vehicle: Represents vehicles (Car, Bike, Truck, etc.).

  5. Ticket: Issued when a vehicle enters the parking lot.

  6. Payment System: Calculates and processes payments.


πŸ“ Design Patterns Used

  1. Factory Pattern: To create vehicle and spot objects dynamically.

  2. Strategy Pattern: To calculate parking fees for different vehicle types.

  3. Singleton Pattern: To ensure only one instance of the ParkingLot class exists.


πŸ“ Class Diagram

diffCopy codeParkingLot
- List<ParkingFloor> floors
- Map<String, ParkingSpot> occupiedSpots
+ parkVehicle(vehicle: Vehicle): Ticket
+ leaveVehicle(ticket: Ticket): double

ParkingFloor
- List<ParkingSpot> spots
+ getAvailableSpot(vehicleType: VehicleType): ParkingSpot

ParkingSpot
- String id
- VehicleType type
- boolean isOccupied
+ assignVehicle(vehicle: Vehicle)
+ removeVehicle()

Vehicle
- String licensePlate
- VehicleType type

Ticket
- String id
- Vehicle vehicle
- Date entryTime

Payment
+ calculateFee(ticket: Ticket): double

VehicleType: Enum { CAR, BIKE, TRUCK }

πŸ”„Step by step Implementation

import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

// Enum for Vehicle Types
enum VehicleType {
    CAR, BIKE, TRUCK
}

// Vehicle Class
class Vehicle {
    private String licensePlate;
    private VehicleType type;

    public Vehicle(String licensePlate, VehicleType type) {
        this.licensePlate = licensePlate;
        this.type = type;
    }

    public VehicleType getType() {
        return type;
    }

    public String getLicensePlate() {
        return licensePlate;
    }
}

// Parking Spot Class
class ParkingSpot {
    private String id;
    private VehicleType type;
    private boolean isOccupied;
    private Vehicle currentVehicle;
    private final Lock lock = new ReentrantLock();

    public ParkingSpot(String id, VehicleType type) {
        this.id = id;
        this.type = type;
        this.isOccupied = false;
    }

    public boolean assignVehicle(Vehicle vehicle) {
        lock.lock();
        try {
            if (!isOccupied && vehicle.getType() == type) {
                this.currentVehicle = vehicle;
                isOccupied = true;
                return true;
            }
            return false;
        } finally {
            lock.unlock();
        }
    }

    public void removeVehicle() {
        lock.lock();
        try {
            this.currentVehicle = null;
            isOccupied = false;
        } finally {
            lock.unlock();
        }
    }

    public boolean isAvailable() {
        lock.lock();
        try {
            return !isOccupied;
        } finally {
            lock.unlock();
        }
    }
}

// Parking Floor Class
class ParkingFloor {
    private String id;
    private List<ParkingSpot> spots;

    public ParkingFloor(String id, List<ParkingSpot> spots) {
        this.id = id;
        this.spots = spots;
    }

    public ParkingSpot getAvailableSpot(VehicleType type) {
        for (ParkingSpot spot : spots) {
            if (spot.getType() == type && spot.isAvailable()) {
                return spot;
            }
        }
        return null;
    }
}

// Ticket Class
class Ticket {
    private String id;
    private Vehicle vehicle;
    private Date entryTime;

    public Ticket(Vehicle vehicle) {
        this.id = UUID.randomUUID().toString();
        this.vehicle = vehicle;
        this.entryTime = new Date();
    }

    public Date getEntryTime() {
        return entryTime;
    }

    public Vehicle getVehicle() {
        return vehicle;
    }
}

// Payment Strategy Interface
interface PaymentStrategy {
    double calculateFee(long duration);
}

class CarPaymentStrategy implements PaymentStrategy {
    public double calculateFee(long duration) {
        return duration * 5.0;
    }
}

class BikePaymentStrategy implements PaymentStrategy {
    public double calculateFee(long duration) {
        return duration * 2.0;
    }
}

// Parking Lot Class
class ParkingLot {
    private static ParkingLot instance;
    private List<ParkingFloor> floors;
    private Map<String, ParkingSpot> occupiedSpots;

    private ParkingLot() {
        this.floors = new ArrayList<>();
        this.occupiedSpots = new HashMap<>();
    }

    public static ParkingLot getInstance() {
        if (instance == null) {
            instance = new ParkingLot();
        }
        return instance;
    }

    public void addFloor(ParkingFloor floor) {
        floors.add(floor);
    }

    public Ticket parkVehicle(Vehicle vehicle) {
        for (ParkingFloor floor : floors) {
            ParkingSpot spot = floor.getAvailableSpot(vehicle.getType());
            if (spot != null && spot.assignVehicle(vehicle)) {
                occupiedSpots.put(vehicle.getLicensePlate(), spot);
                return new Ticket(vehicle);
            }
        }
        throw new IllegalStateException("No available spot!");
    }

    public double leaveVehicle(Ticket ticket) {
        ParkingSpot spot = occupiedSpots.get(ticket.getVehicle().getLicensePlate());
        if (spot != null) {
            long duration = (new Date().getTime() - ticket.getEntryTime().getTime()) / 3600000;
            PaymentStrategy strategy = ticket.getVehicle().getType() == VehicleType.CAR
                ? new CarPaymentStrategy()
                : new BikePaymentStrategy();
            double fee = strategy.calculateFee(duration);
            spot.removeVehicle();
            occupiedSpots.remove(ticket.getVehicle().getLicensePlate());
            return fee;
        }
        throw new IllegalStateException("Invalid Ticket!");
    }
}

// Demo Class
public class ParkingLotDemo {
    public static void main(String[] args) throws InterruptedException {
        ParkingLot lot = ParkingLot.getInstance();

        // Setup parking lot
        ParkingFloor floor1 = new ParkingFloor("F1", Arrays.asList(
            new ParkingSpot("S1", VehicleType.CAR),
            new ParkingSpot("S2", VehicleType.BIKE)
        ));
        lot.addFloor(floor1);

        // Park a vehicle
        Vehicle car = new Vehicle("CAR123", VehicleType.CAR);
        Ticket ticket = lot.parkVehicle(car);

        // Simulate parking duration
        Thread.sleep(2000);

        // Leave the parking lot
        double fee = lot.leaveVehicle(ticket);
        System.out.println("Parking Fee: $" + fee);
    }
}
Β