Functional Scope (In-Scope)
- Multi-Floor Parking: The system should support multiple floors with diverse spot types (Motorcycle, Compact, Large).
- Spot Allocation: Match incoming vehicles automatically to their correct spot type (e.g., Cars in Compact/Large, Trucks only in Large).
- Ticket Management: Generate a unique, timestamped ticket at entry gates and collect it at exit gates.
- Fee Calculation: Pluggable hourly or flat-rate dynamic pricing based on total parked duration.
- Payment Processing: Support cards, cash, or mobile payments with mock verification logic.
Explicit Boundaries (Out-of-Scope)
- No Real Database Integration: All storage is managed in-memory (singleton orchestrators, local maps) to focus purely on object models.
- No UI Layer: Interacted with via a simple programmatic test harness or terminal controller.
- Simplified Hardware Integrations: Barrier arm gates, ticket printers, and display boards are represented by stateless interface mocks.
Below is a clean, multi-language codebase blueprint showcasing our design patterns and thread-safety strategies in Java and Python:
// ─── JAVA BLUEPRINT ──────────────────────────────────────────────────────────
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
enum VehicleType { MOTORCYCLE, CAR, TRUCK }
enum SpotType { MOTORCYCLE, COMPACT, LARGE }
abstract class Vehicle {
private final String licensePlate;
private final VehicleType type;
protected Vehicle(String licensePlate, VehicleType type) {
this.licensePlate = licensePlate;
this.type = type;
}
public String getLicensePlate() { return licensePlate; }
public VehicleType getType() { return type; }
}
class Motorcycle extends Vehicle {
public Motorcycle(String licensePlate) { super(licensePlate, VehicleType.MOTORCYCLE); }
}
class Car extends Vehicle {
public Car(String licensePlate) { super(licensePlate, VehicleType.CAR); }
}
class Truck extends Vehicle {
public Truck(String licensePlate) { super(licensePlate, VehicleType.TRUCK); }
}
class ParkingSpot {
private final String id;
private final SpotType type;
private Vehicle currentVehicle;
private boolean isFree = true;
public ParkingSpot(String id, SpotType type) {
this.id = id;
this.type = type;
}
public synchronized boolean isAvailable() { return isFree; }
public synchronized boolean reserve(Vehicle vehicle) {
if (!isFree) return false;
this.currentVehicle = vehicle;
this.isFree = false;
return true;
}
public synchronized void release() {
this.currentVehicle = null;
this.isFree = true;
}
public String getId() { return id; }
public SpotType getType() { return type; }
public Vehicle getCurrentVehicle() { return currentVehicle; }
}
class ParkingFloor {
private final String name;
private final List<ParkingSpot> spots = new CopyOnWriteArrayList<>();
public ParkingFloor(String name) {
this.name = name;
}
public void addSpot(ParkingSpot spot) {
spots.add(spot);
}
public List<ParkingSpot> getSpots() { return spots; }
public String getName() { return name; }
}
interface PricingStrategy {
double calculateFee(long durationMs);
}
class HourlyPricing implements PricingStrategy {
private final double hourlyRate;
public HourlyPricing(double rate) { this.hourlyRate = rate; }
@Override
public double calculateFee(long durationMs) {
double hours = Math.ceil(durationMs / 3600000.0);
return Math.max(1.0, hours) * hourlyRate;
}
}
class Ticket {
private static final AtomicInteger counter = new AtomicInteger(1);
private final String ticketId;
private final Vehicle vehicle;
private final ParkingSpot spot;
private final long entryTime;
private long exitTime;
private double fee;
private boolean isPaid;
public Ticket(Vehicle vehicle, ParkingSpot spot) {
this.ticketId = "TKT-" + counter.getAndIncrement();
this.vehicle = vehicle;
this.spot = spot;
this.entryTime = System.currentTimeMillis();
}
public String getTicketId() { return ticketId; }
public Vehicle getVehicle() { return vehicle; }
public ParkingSpot getSpot() { return spot; }
public long getEntryTime() { return entryTime; }
public long getExitTime() { return exitTime; }
public void setExitTime(long exitTime) { this.exitTime = exitTime; }
public double getFee() { return fee; }
public void setFee(double fee) { this.fee = fee; }
public boolean isPaid() { return isPaid; }
public void pay() { this.isPaid = true; }
}
enum PaymentType { CASH, CARD, UPI }
abstract class Payment {
private final String transactionId;
protected double amount;
protected Payment(double amount) {
this.transactionId = "TXN-" + UUID.randomUUID().toString().substring(0, 8).toUpperCase();
this.amount = amount;
}
public abstract boolean process();
public String getTransactionId() { return transactionId; }
}
class CashPayment extends Payment {
public CashPayment(double amount) { super(amount); }
@Override
public boolean process() {
System.out.printf("[Cash] Processed payment of $%.2f via transaction: %s\n", amount, getTransactionId());
return true;
}
}
class CardPayment extends Payment {
public CardPayment(double amount) { super(amount); }
@Override
public boolean process() {
System.out.printf("[Card] Processed payment of $%.2f via transaction: %s\n", amount, getTransactionId());
return true;
}
}
class UPIPayment extends Payment {
public UPIPayment(double amount) { super(amount); }
@Override
public boolean process() {
System.out.printf("[UPI] Processed payment of $%.2f via transaction: %s\n", amount, getTransactionId());
return true;
}
}
class PaymentFactory {
public static Payment createPayment(PaymentType type, double amount) {
switch (type) {
case CASH: return new CashPayment(amount);
case CARD: return new CardPayment(amount);
case UPI: return new UPIPayment(amount);
default: throw new IllegalArgumentException("Unknown payment type: " + type);
}
}
}
class ParkingLot {
private static volatile ParkingLot instance;
private final String name;
private final List<ParkingFloor> floors = new CopyOnWriteArrayList<>();
private final ConcurrentHashMap<String, Ticket> activeTickets = new ConcurrentHashMap<>();
private PricingStrategy pricingStrategy = new HourlyPricing(5.0);
private ParkingLot(String name) {
this.name = name;
}
public static ParkingLot getInstance(String name) {
if (instance == null) {
synchronized (ParkingLot.class) {
if (instance == null) {
instance = new ParkingLot(name);
}
}
}
return instance;
}
public void addFloor(ParkingFloor floor) {
floors.add(floor);
}
public void setPricingStrategy(PricingStrategy strategy) {
this.pricingStrategy = strategy;
}
private boolean isSpotCompatible(VehicleType vehicleType, SpotType spotType) {
switch (vehicleType) {
case MOTORCYCLE: return spotType == SpotType.MOTORCYCLE || spotType == SpotType.COMPACT || spotType == SpotType.LARGE;
case CAR: return spotType == SpotType.COMPACT || spotType == SpotType.LARGE;
case TRUCK: return spotType == SpotType.LARGE;
default: return false;
}
}
public synchronized Ticket parkVehicle(Vehicle vehicle) {
for (ParkingFloor floor : floors) {
for (ParkingSpot spot : floor.getSpots()) {
if (spot.isAvailable() && isSpotCompatible(vehicle.getType(), spot.getType())) {
if (spot.reserve(vehicle)) {
Ticket ticket = new Ticket(vehicle, spot);
activeTickets.put(ticket.getTicketId(), ticket);
System.out.printf("[ParkingSuccess] %s (%s) parked at spot %s on floor %s. Ticket: %s\n",
vehicle.getType(), vehicle.getLicensePlate(), spot.getId(), floor.getName(), ticket.getTicketId());
return ticket;
}
}
}
}
System.out.printf("[ParkingFailed] No compatible spot available for %s (%s)\n", vehicle.getType(), vehicle.getLicensePlate());
return null;
}
public synchronized boolean checkoutVehicle(String ticketId, PaymentType paymentType) {
Ticket ticket = activeTickets.get(ticketId);
if (ticket == null) {
System.out.println("[CheckoutError] Ticket ID " + ticketId + " not found.");
return false;
}
ticket.setExitTime(System.currentTimeMillis() + 7200000); // Mocks 2 hours parking
long duration = ticket.getExitTime() - ticket.getEntryTime();
double fee = pricingStrategy.calculateFee(duration);
ticket.setFee(fee);
Payment payment = PaymentFactory.createPayment(paymentType, fee);
if (payment.process()) {
ticket.pay();
ticket.getSpot().release();
activeTickets.remove(ticketId);
System.out.printf("[CheckoutSuccess] Ticket %s resolved. Fee: $%.2f. Spot %s is vacant.\n",
ticketId, fee, ticket.getSpot().getId());
return true;
}
return false;
}
}
public class Main {
public static void main(String[] args) {
ParkingLot lot = ParkingLot.getInstance("Grand Arena");
ParkingFloor f1 = new ParkingFloor("Floor 1");
f1.addSpot(new ParkingSpot("F1-M1", SpotType.MOTORCYCLE));
f1.addSpot(new ParkingSpot("F1-C1", SpotType.COMPACT));
f1.addSpot(new ParkingSpot("F1-L1", SpotType.LARGE));
lot.addFloor(f1);
Vehicle car1 = new Car("KA-01-AB-1234");
Vehicle moto1 = new Motorcycle("KA-01-XYZ-567");
Ticket t1 = lot.parkVehicle(car1);
Ticket t2 = lot.parkVehicle(moto1);
lot.checkoutVehicle(t1.getTicketId(), PaymentType.UPI);
lot.checkoutVehicle(t2.getTicketId(), PaymentType.CARD);
}
}