Requirements


Functional Requirements:


  • Assign first-fitting spot - On arrival, find the earliest free spot that can fit the vehicle size across floors.
  • Issue ticket on entry - Generate unique ticket with vehicle, spot, and entry time.
  • Compute fee on exit - Calculate duration and apply active PricingStrategy.
  • Lot full handling - If no suitable spot exists, indicate 'Lot Full'.


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.





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 separate responsibility. The design is easier to understand and maintain, though it introduces more classes than a procedural design.


The design also supports extensibility using interfaces like PricingStrategy with different pricing models that can implement the pricing strategy. It makes the system support extensive pricing ranges like hourly, weekly, etc. The trade-off introduces extra abstraction and classes, but it provides more extensibility.


Currently, the vehicle process is handled by the Exit Gate, which handles payment processing and ticket closing, but if the payment method changes, then we would have to modify the Exit Gate, which violates the Open–Closed Principle and the Dependency Inversion Principle. We made the implementation simple, but it would result in more modifications in the existing logic of the class.


Concurrency has been added for the ParkingSpot to avoid assigning the same spot to more than one vehicle. This makes the system more reliable and less prone to failure and incorrect spot assignment. But this also increases implementation complexity for the system.


SOLID Compliance:

SRP (Single Responsibility Principle):

We have designed our classes to follow the Single Responsibility Principle. Most of our classes follow that. Some examples are: the ParkingSpot class's only responsibility is to manage a particular parking spot. Similarly, other classes follow a similar pattern.


OCP (Open–Closed Principle):

All classes follow the Open–Closed Principle except Payment and ExitGate. If new payment methods are introduced, we would need to modify both of these classes. To avoid this issue and keep the system extensible, we can introduce an abstraction (such as a PaymentStrategy interface) so that new payment methods can be added without modifying existing code.


LSP (Liskov Substitution Principle):

We are using the Liskov Substitution Principle with PricingStrategy, where the different pricing subtypes can substitute the base type without breaking the functionality.


ISP (Interface Segregation Principle):

We applied the Interface Segregation Principle to make the system more decoupled, clean, and maintainable. Instead of creating large interfaces, we designed small and focused interfaces. For example, the PricingStrategy interface is responsible only for calculating the price for different pricing strategies. This ensures that classes implement only the methods they actually need, and no class is forced to implement methods that are not relevant to its responsibility.


DIP (Dependency Inversion Principle):

We have used the Dependency Inversion Principle partially in our system to avoid tight coupling between the classes and strong dependency. Most of our classes follow the Dependency Inversion Principle. We would need to improve the ExitGate to inject the Payment object through the constructor. Also, we could improve it further by creating more interfaces, so the high-level and low-level modules depend on abstractions.


Scalability Consideration:

The system is scalable and handles concurrency in the ParkingSpot component to prevent race conditions. It ensures that multiple vehicles cannot occupy the same parking spot while allowing the system to safely process multiple requests concurrently.


Our system is designed to horizontally scale. It will successfully accommodate the addition of new floors and spots. It will be able to handle different pricing strategies easily if any new pricing strategy is introduced later.


We have some bottlenecks right now. The locking mechanism is designed to only handle a single instance of the application (one single server deployment). What if the application is deployed on multiple servers? The current locking system may result in failure. So, we need to use a distributed locking mechanism in our system.


Extensibility Consideration:

Right now, our system is extensible in most cases like pricing strategy, but it also has some extensibility limitations. For example, what if a new payment system is added? Then the current Payment and ExitGate would need modification. To fix these issues, we would need to create the PaymentStrategy interface and implement it with different payment method classes, and pass the payment as dependency injection in the Exit Gate to avoid tight coupling.


Future Improvement:

There are some future improvements needed. I would like these future improvements to be implemented:


Use a PaymentStrategy interface and implement that with different payment methods, also pass this to ExitGate as dependency injection through the constructor.


Update the locking mechanism from single instance to a distributed instance locking mechanism and concurrency handling.


I would also like to further refactor the Exit Gate class if complexity increases. Right now, it is handling ticket closing, payment processing, and the vehicle exit process, and making the spot available.