Design A Parking Lot System
π 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
Parking Lot: Manages all parking floors and spots.
Parking Floor: Represents each floor and its spots.
Parking Spot: Represents an individual parking spot.
Vehicle: Represents vehicles (Car, Bike, Truck, etc.).
Ticket: Issued when a vehicle enters the parking lot.
Payment System: Calculates and processes payments.
π Design Patterns Used
Factory Pattern: To create vehicle and spot objects dynamically.
Strategy Pattern: To calculate parking fees for different vehicle types.
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);
}
}