📌Problem Statement
Design a parking lot system with multiple floors, different vehicle types, and automated ticketing.
📌Requirements
Functional
Constraints
📌Class Design
Core Classes
public enum VehicleType { MOTORCYCLE, CAR, TRUCK}public enum SpotType { MOTORCYCLE, COMPACT, LARGE}public enum TicketStatus { ACTIVE, PAID, LOST}Vehicle Class
public abstract class Vehicle { private String licensePlate; private VehicleType type; public Vehicle(String licensePlate, VehicleType type) { this.licensePlate = licensePlate; this.type = type; } public abstract boolean canFitIn(SpotType spotType);}public class Car extends Vehicle { public Car(String licensePlate) { super(licensePlate, VehicleType.CAR); } public boolean canFitIn(SpotType spotType) { return spotType == SpotType.COMPACT || spotType == SpotType.LARGE; }}public class Motorcycle extends Vehicle { public Motorcycle(String licensePlate) { super(licensePlate, VehicleType.MOTORCYCLE); } public boolean canFitIn(SpotType spotType) { return true; }}public class Truck extends Vehicle { public Truck(String licensePlate) { super(licensePlate, VehicleType.TRUCK); } public boolean canFitIn(SpotType spotType) { return spotType == SpotType.LARGE; }}ParkingSpot Class
public class ParkingSpot { private String spotId; private SpotType type; private Vehicle vehicle; private int floor; public ParkingSpot(String spotId, SpotType type, int floor) { this.spotId = spotId; this.type = type; this.floor = floor; } public boolean isAvailable() { return vehicle == null; } public boolean canFit(Vehicle vehicle) { return isAvailable() && vehicle.canFitIn(type); } public void park(Vehicle vehicle) { if (!canFit(vehicle)) { throw new IllegalStateException("Cannot park here"); } this.vehicle = vehicle; } public Vehicle unpark() { Vehicle v = this.vehicle; this.vehicle = null; return v; }}Ticket and Payment
public class Ticket { private String ticketId; private Vehicle vehicle; private ParkingSpot spot; private LocalDateTime entryTime; private LocalDateTime exitTime; private TicketStatus status; private double amount; public Ticket(Vehicle vehicle, ParkingSpot spot) { this.ticketId = UUID.randomUUID().toString(); this.vehicle = vehicle; this.spot = spot; this.entryTime = LocalDateTime.now(); this.status = TicketStatus.ACTIVE; } public double calculateFee(PricingStrategy strategy) { long hours = ChronoUnit.HOURS.between(entryTime, LocalDateTime.now()); return strategy.calculate(hours, vehicle.getType()); }}public interface PricingStrategy { double calculate(long hours, VehicleType type);}public class HourlyPricing implements PricingStrategy { private Map<VehicleType, Double> rates; public double calculate(long hours, VehicleType type) { return Math.max(1, hours) * rates.get(type); }}ParkingLot - Main Class
public class ParkingLot { private static ParkingLot instance; private List<ParkingFloor> floors; private Map<String, Ticket> activeTickets; private PricingStrategy pricingStrategy; private ParkingLot() { floors = new ArrayList<>(); activeTickets = new ConcurrentHashMap<>(); } public static synchronized ParkingLot getInstance() { if (instance == null) { instance = new ParkingLot(); } return instance; } public Ticket parkVehicle(Vehicle vehicle) { ParkingSpot spot = findAvailableSpot(vehicle); if (spot == null) { throw new ParkingFullException("No spot available"); } spot.park(vehicle); Ticket ticket = new Ticket(vehicle, spot); activeTickets.put(ticket.getTicketId(), ticket); return ticket; } public double unparkVehicle(String ticketId) { Ticket ticket = activeTickets.get(ticketId); if (ticket == null) { throw new InvalidTicketException("Ticket not found"); } double fee = ticket.calculateFee(pricingStrategy); ticket.getSpot().unpark(); activeTickets.remove(ticketId); return fee; } private ParkingSpot findAvailableSpot(Vehicle vehicle) { for (ParkingFloor floor : floors) { ParkingSpot spot = floor.findSpot(vehicle); if (spot != null) return spot; } return null; }}📌Design Patterns Used
📌Interview Tips
This is a classic LLD question asked at Amazon, Google, and Flipkart.