My Solution for Design a Parking Lot
by zephyr_kaleidoscope326
Requirements
Functional Requirements
- Assign the first fitting parking spot across floors when a vehicle arrives.
- Generate a parking ticket containing vehicle details, assigned spot, and entry time.
- Calculate parking fee on exit using PricingStrategy.
- Handle the "Lot Full" scenario when no suitable spot exists.
- Process parking payment using different payment methods.
- Support adding and removing parking floors dynamically.
Non-Functional Requirements
- System must be scalable to support multiple floors and many vehicles.
- System must be extensible so new vehicle types, pricing strategies, or payment methods can be added easily.
- The system must handle concurrency safely to prevent multiple vehicles from being assigned the same spot.
- The system must maintain the correctness of the parking state and fee calculation.
Core Objects & Relationships
Based on the requirements and use cases, identify the main objects of the system and analyze how they interact and relate to each other...
Core Objects:
- ParkingLot
- ParkingFloor
- ParkingSpot
- PricingStrategy
- Vehicle
- EntryGate
- ExitGage
- Ticket
- Payment
Relationships
ParkingLot
- ParkingLot has many ParkingFloor (composition)
- ParkingLot has one PricingStrategy
- ParkingLot has many EntryGate
- ParkingLot has many ExitGate
ParkingFloor
- ParkingFloor has many ParkingSpot (composition)
ParkingSpot
- ParkingSpot can be associated with at most one Vehicle at a time (0..1)
Ticket
- Each Ticket is associated with exactly one Vehicle
- Each Ticket is associated with exactly one ParkingSpot
- Each Ticket may have zero or one Payment (0..1)
EntryGate
- EntryGate creates/issues Ticket
- EntryGate interacts with ParkingLot to allocate ParkingSpot
- (dependency/behavioral interaction)
ExitGate
- ExitGate uses PricingStrategy to calculate charges
- ExitGate processes Payment
- ExitGate closes Ticket
PricingStrategy
- PricingStrategy defines the algorithm to calculate parking fees
- Different implementations can exist (e.g., hourly, flat rate)
Relationship Summary by Type:
Composition:
ParkingLot -> ParkingFloor
ParkingFloor -> PrkingSpot
Association:
ParkingSpot -> Vehicle
Ticket -> Vehicle
Ticket -> ParkingSpot
Ticket -> payment
Dependency:
ExitGate -> PricingStrategy
ExitGate -> Payment
ExitGate -> Ticket
APIs & Class Members
For each class, define the attributes (data) it will hold and the methods (functions) that operate on the attributes. Ensure they align with the object's responsibilities and adhere to the principle of encapsulation. Write your code in the code editor below.
Vehicle
Attributes:
- licensePlate : String
- vehicleType : VehicleType
Methods:
- getLicensePlate() : String
- getVehicleType() : VehicleType
Car (implements Vehicle)
Attributes:
- licensePlate : String
Methods:
- getLicensePlate() : String
- getVehicleType() : VehicleType
Truck (implements Vehicle)
Attributes:
- licensePlate : String
Methods:
- getLicensePlate() : String
- getVehicleType() : VehicleType
ParkingSpot
Attributes:
- id : String
- spotType : SpotType
- assignedVehicle : Vehicle
- isAvailable : boolean
Methods:
- canFit(Vehicle vehicle) : boolean
- assignVehicle(Vehicle vehicle) : void
- removeVehicle() : void
- getId() : String
- getSpotType() : SpotType
- isAvailable() : boolean
- getAssignedVehicle() : Vehicle
ParkingFloor
Attributes:
- floorId : int
- spots : List<ParkingSpot>
Methods:
- findAvailableSpot(Vehicle vehicle) : Optional<ParkingSpot>
- addSpot(ParkingSpot spot) : void
- removeSpot(ParkingSpot spot) : void
ParkingLot
Attributes:
- floors : List<ParkingFloor>
- pricingStrategy : PricingStrategy
Methods:
- findAvailableSpot(Vehicle vehicle) : Optional<ParkingSpot>
- getPricingStrategy() : PricingStrategy
Ticket
Attributes:
- ticketId : int
- vehicle : Vehicle
- spot : ParkingSpot
- payment : Payment
- entryTime : LocalDateTime
- exitTime : LocalDateTime
Methods:
- closeTicket() : void
- attachPayment(Payment payment) : void
- getSpot() : ParkingSpot
- getEntryTime() : LocalDateTime
- getExitTime() : LocalDateTime
EntryGate
Attributes:
- ticketCounter : int
Methods:
- issueTicket(Vehicle vehicle, ParkingLot parkingLot) : Ticket
ExitGate
Attributes:
- paymentProcessor : PaymentProcessor
Methods:
- processVehicleExit(Ticket ticket, ParkingLot parkingLot) : Payment
Payment
Attributes:
- amount : double
- status : PaymentStatus
Methods:
- markSuccess() : void
- getStatus() : PaymentStatus
PaymentProcessor
Methods:
- processPayment(double amount) : Payment
CardPaymentProcessor (implements PaymentProcessor)
Methods:
- processPayment(double amount) : Payment
UpiPaymentProcessor (implements PaymentProcessor)
Methods:
- processPayment(double amount) : Payment
PricingStrategy
Methods:
- calculateFee(Ticket ticket) : double
HourlyPricingStrategy (implements PricingStrategy)
Attributes:
- hourlyRate : double
Methods:
- calculateFee(Ticket ticket) : double
Deep Dive
Explain design tradeoffs you considered. Check and explain whether your design adheres to SOLID principles. Explain how your design can handle changes in scale and whether it would be easy to extend with new functionalities. Identify areas for future improvement...
Design Trade-off
The design prioritizes clarity and separation of responsibilities. The system is modeled using domain entities such as ParkingSpot, Payment, ParkingLot, PricingStrategy, etc. Each of these entities is designed to represent a specific responsibility within the parking system. This makes the system easier to understand and maintain, although it introduces more classes compared to a simpler procedural design.
The design also supports extensibility through interfaces such as PricingStrategy, which allows different pricing models to be implemented without modifying the existing system. This enables the system to support different pricing models such as hourly or flat pricing. The trade-off is that additional abstractions and classes are introduced, which increases complexity slightly but significantly improves extensibility.
Payment processing is handled through the PaymentProcessor interface. The ExitGate depends on this abstraction rather than a concrete payment implementation. This allows new payment methods to be added without modifying the ExitGate logic, which improves extensibility and reduces coupling between components.
Concurrency handling is implemented at the ParkingSpot level using a ReentrantLock to prevent multiple vehicles from being assigned to the same spot simultaneously. This improves the reliability of the system but introduces additional complexity due to synchronization management.
SOLID Compliance
SRP (Single Responsibility Principle)
The classes are designed to follow the Single Responsibility Principle. Each class is responsible for a specific part of the system.
Examples:
ParkingSpotmanages the state of a specific parking spot.Ticketrepresents a parking session for a vehicle.ParkingFloormanages the collection of parking spots on a floor.ExitGatehandles the vehicle exit process.
This separation of responsibilities improves maintainability and readability.
OCP (Open–Closed Principle)
The system follows the Open–Closed Principle by using abstractions that allow behavior to be extended without modifying existing classes.
Examples include:
PricingStrategyallows new pricing models to be added.PaymentProcessorallows new payment methods to be introduced.
This makes the system open for extension but closed for modification.
LSP (Liskov Substitution Principle)
The system follows the Liskov Substitution Principle through the use of the PricingStrategy interface. Different implementations such as HourlyPricingStrategy can substitute the base PricingStrategy type without affecting the correctness of the system.
ISP (Interface Segregation Principle)
The system uses small and focused interfaces such as PricingStrategy and PaymentProcessor. These interfaces define only the behavior required by implementing classes, ensuring that classes are not forced to implement methods that they do not need.
This keeps the design clean and avoids unnecessary dependencies between components.
DIP (Dependency Inversion Principle)
The design follows the Dependency Inversion Principle by ensuring that high-level modules depend on abstractions rather than concrete implementations.
For example, ExitGate depends on the PaymentProcessor interface instead of concrete payment classes. This reduces coupling and allows payment implementations to change without affecting higher-level components.
Scalability Consideration
The system is designed to scale by allowing additional parking floors and parking spots to be added without changing the core architecture.
Concurrency is handled at the ParkingSpot level using locks to ensure that multiple vehicles cannot occupy the same spot simultaneously. This helps maintain consistency in multi-threaded environments.
However, the current locking mechanism works only within a single application instance. If the system is deployed across multiple servers, this mechanism would not prevent race conditions across instances. In such cases, a distributed locking mechanism such as Redis-based locks or database row-level locking would be required.
Extensibility Consideration
The system is extensible in several areas:
- New pricing models can be added by implementing the
PricingStrategyinterface. - New payment methods can be introduced by implementing the
PaymentProcessorinterface. - New vehicle types or parking spot types can be added with minimal changes to existing components.
This design allows the system to evolve as requirements change.
Future Improvements
Several improvements could further enhance the system:
- Implement a distributed locking mechanism to support deployments across multiple servers.
- Introduce a spot indexing structure such as
Map<VehicleType, Queue<ParkingSpot>>to improve parking spot lookup performance. - Support EV charging spots as a new type of parking spot.
- Introduce a reservation system that allows users to reserve parking spots in advance.
- Implement dynamic pricing strategies that adjust parking rates based on occupancy or time of day.