Requirements

Determine the different ways the system will be used. This includes main functions the system needs to perform and who will use it.

Parking Spot Management:

Vehicle Management:

Parking Assignment:

Fee Calculation:

User Interface:


Define Core Objects

Based on the requirements and use cases, identify the main objects of the system...





Analyze Relationships

Determine how these objects will interact with each other to fulfill the use cases...

  1. ParkingLot and ParkingFloor:
    • The ParkingLot contains multiple ParkingFloor objects. It manages the overall parking facility and delegates the task of finding an available spot to the appropriate floor.
  2. ParkingFloor and ParkingSpot:
    • Each ParkingFloor contains multiple ParkingSpot objects. The floor finds and assigns an available spot to a vehicle based on its size.
  3. ParkingSpot and Vehicle:
    • ParkingSpot is either occupied by a Vehicle or is vacant. When occupied, the spot stores a reference to the vehicle.
  4. Vehicle and Ticket:
    • When a vehicle is parked, a Ticket is generated, recording the entry time, the assigned spot, and the vehicle. The ticket is used to calculate the parking fee.





Establish Hierarchy

Design inheritance trees where applicable to promote code reuse and polymorphism. This step involves identifying common attributes and behaviors that can be abstracted into parent classes...

  1. ParkingSpot:
    • We'll create a base class called ParkingSpot that will contain common attributes like spotIdsize, and isOccupied.
    • We can then create subclasses for different types of parking spots if needed, such as CompactSpotLargeSpot, and HandicappedSpot, each inheriting from ParkingSpot.
  2. Vehicle:
    • A base class Vehicle will represent general attributes like licensePlate and size.
    • Subclasses like CarBike, and Truck can inherit from Vehicle to handle vehicle-specific details.
  3. User:
    • The User class will serve as a base class with common attributes like userIdname, and role.
    • Subclasses such as AdminAttendant, and RegularUser can inherit from User to implement role-specific behaviors.
  4. Ticket and Payment:
    • These classes do not require a hierarchical structure, as they are specific to their tasks. However, they could implement common interfaces if the system were to grow, supporting different types of payments or ticket systems.





Design Patterns

Consider using design patterns (e.g., Factory, Singleton, Observer, Strategy) that fit the problem...

Singleton Design Pattern

The Singleton Design Pattern has been implemented for the Parking Lot class, ensuring that only one instance of the Parking Lot is shared throughout the application. This is crucial for managing the parking spots consistently and avoiding conflicts that occur when multiple instances attempt to access shared resources.


class ParkingLot{ private static ParkingLot instance=null; private ParkingLot(){} ParkingLot getInstance() { if(instance==null)return new ParkingLot(); else return instance; } //other methods like //adding spots // assignSpots }

Factory Design Pattern

The VehicleFactory utilizes the Factory Design Pattern for effective object creation of various vehicle types. This pattern allows for the instantiation of different vehicle objects based on specific conditions, such as the vehicle category (e.g., car, motorcycle, truck) or the required attributes (e.g., capacity, size). This approach promotes loose coupling and enhances the maintainability of the code.

public class VehicleFactory { public static Vehicle createVehicle(String type, String licensePlate) { switch(type.toLowerCase()) { case "car": return new Car(licensePlate); case "bike": return new Bike(licensePlate); case "truck": return new Truck(licensePlate); default: throw new IllegalArgumentException("Unknown vehicle type"); } } }

Strategy Design Pattern

The Strategy Design Pattern is implemented for defining the pricing strategy for different vehicle types. For instance, in our example, cars have a discounted fee strategy, while other vehicles work with a default pricing strategy. This separation allows for flexible pricing adjustments based on vehicle type and ensures the underlying logic can be modified independently of the vehicle classes.

interface FeeStategy{ static int calculateFee1(Date startDate, Date endDate) { return 0; } } class DiscountFee implements FeeStategy { public static int calculateFee1(Date startDate, Date endDate) { // return endDate-startDate; return 9; } } class DefaultStrategy implements FeeStategy{ public static int calculateFee1(Date startDate, Date endDate) { // String i = endDate - startDate; // return i.length(); return 0; } } public class Ticket{ Feestrategy feestrategy; int calculateFee() { if(vehicle.getType()== VehicleType.CAR) return DiscountFee.calculateFee1(EntryTime,new Date()); else return DefaultStrategy.calculateFee1(EntryTime,new Date()); } }

Observer Design Pattern

The Observer Design Pattern can be employed here to notify subscribers (observers) when a parking spot becomes available or when the allocated parking time for a vehicle is nearing completion. By using this design pattern, we ensure that all interested parties receive timely updates and can react accordingly, which enhances the dynamic interaction within our parking system.



Define Class Members (write code)

Attributes: For each class, define the attributes (data) it will hold...

Methods: Define the methods (functions) that operate on the attributes. Ensure they align with the object's responsibilities and adhere to the principle of encapsulation.


ParkingLot->

public class ParkingLot { private static ParkingLot instance = null; List<ParkingSpot>li=new ArrayList<>(); private ParkingLot() { } void addSpot(){ li.add(new ParkingSpot(null,null,true)); li.add(new ParkingSpot(null,null,true)); li.add(new ParkingSpot(null,null,true)); li.add(new ParkingSpot(null,null,true)); } public static synchronized ParkingLot getInstance() { if(instance == null) { instance=new ParkingLot(); } return instance; } ParkingSpot AssignSpot(Vehicle vehicle) { for(ParkingSpot sp:li) { if(sp.isAvailable==true) { return sp.assignVehcile(vehicle); } } return null; } }

ParkingSpot->

public class ParkingSpot { Vehicle vehicle; VehicleType vehicleType; boolean isAvailable; Ticket ticket; public ParkingSpot(VehicleType vehicleType, Vehicle vehicle, boolean isAvailable) { this.vehicleType = vehicleType; this.vehicle = vehicle; this.isAvailable = isAvailable; } boolean check() { return isAvailable==true; } ParkingSpot assignVehcile(Vehicle vehicle) { this.vehicle = vehicle; this.vehicleType=vehicle.getType(); isAvailable=false; ticket=new Ticket(new Date(),1,this,this.vehicle); return this; } void removeVehicle(Vehicle vehicle) { this.vehicle=null; this.vehicleType=null; isAvailable=true; System.out.println(this.ticket.calculateFee()); } }

Vehicle->

public class Vehicle { String licenceNumber; VehicleType vehicleType; Vehicle(String licenceNumber, VehicleType vehicleType) { this.licenceNumber = licenceNumber; this.vehicleType = vehicleType; } public VehicleType getType() { return vehicleType; } }

Ticket->

interface FeeStategy{ static int calculateFee1(Date startDate, Date endDate) { return 0; } } class DiscountFee implements FeeStategy { public static int calculateFee1(Date startDate, Date endDate) { return 9; } } class DefaultStrategy implements FeeStategy{ public static int calculateFee1(Date startDate, Date endDate) { return 0; } } public class Ticket { int ticketID; Date EntryTime; Date ExitTime; Vehicle vehicle; ParkingSpot parkingSpot; FeeStategy feeStrategy; public Ticket( Date entryTime, int ticketID, ParkingSpot parkingSpot, Vehicle vehicle) { EntryTime = entryTime; this.ticketID = ticketID; this.parkingSpot = parkingSpot; this.vehicle = vehicle; } int calculateFee() { if(vehicle.getType()== VehicleType.CAR) return DiscountFee.calculateFee1(EntryTime,new Date()); else return DefaultStrategy.calculateFee1(EntryTime,new Date()); } }






Adhere to SOLID Guidelines

Check and explain whether your design adheres to solid principles (Ask interviewer what SOLID principle is if you can not recall it.)...


Single Responsibility Principle

Each class in our design has a single responsibility. For example, ParkingLot manages the overall parking lot, ParkingSpot manages an individual spot, and Ticket handles parking tickets. This clear separation ensures that changes in one aspect of the system don’t affect other parts.


Open/Closed Principle (OCP)

The use of inheritance allows for extending functionality without modifying existing classes. For instance, new vehicle types (e.g., ElectricCar) can be added by creating new subclasses of Vehicle without altering the existing Vehicle class. Similarly, new fee calculation strategies can be added by implementing new FeeStrategy classes.


Liskov Substitution Principle (LSP)

Subclasses like CarBike, and Truck can be used interchangeably with the Vehicle superclass. The system’s behavior remains consistent regardless of the specific vehicle type, adhering to LSP.


Interface Segregation Principle

Although we haven't explicitly defined interfaces, our design ensures that classes only contain methods relevant to their specific role. For example, Vehicle only includes methods related to vehicle characteristics, and ParkingSpot focuses on spot management.


Dependency Inversion Principle (DIP)

The use of strategies for fee calculation (e.g., FeeStrategy interface) allows high-level classes like Ticket to depend on abstractions rather than concrete implementations. This makes the system more flexible and easier to modify or extend.



Consider Scalability and Flexibility

Explain how your design can handle changes in scale and whether it would be easily to extend with new functionalities...

Scalability

Horizontal Scaling (Adding More Floors and Spots): New floors can be added by simply instantiating additional ParkingFloor objects and adding them to the ParkingLot. Similarly, new spots can be added to any floor by adding ParkingSpot objects to the corresponding ParkingFloor.

Vertical Scalling -> Database can be shard and partioned for the floor wise. It will make our database distributed and can handle large amount of request to out database .


Flexibility


Adding New Vehicle Types

The system's design supports the easy integration of new vehicle types through the use of the Factory Design Pattern. By creating subclasses of a shared Vehicle interface, such as ElectricCar, developers can introduce new vehicle types without altering existing code. This modular architecture, coupled with configuration files for dynamic attribute loading, ensures the system remains maintainable and adaptable.


Adding New Payment Methods

The Strategy Design Pattern facilitates the addition of new payment methods by allowing them to be implemented as interchangeable strategies. For instance, adding a DigitalWallet payment option involves simply creating a new class that adheres to the PaymentStrategy interface. The use of configuration files also enables dynamic updates, allowing administrators to integrate new payment methods without needing to modify the codebase.





Create/Explain your diagram(s)

Try creating a class, flow, state and/or sequence diagram using the diagramming tool. Mermaid flow diagrams can be used to represent system use cases. You can ask the interviewer bot to create a starter diagram if unfamiliar with the tool. Briefly explain your diagrams if necessary...






Future improvements

Critically examine your design for any flaws or areas for future improvement.


Automatic Spot Assignment ->Our System should assign automatic spot to the user efficiently means depending upon the vehicle entry we can assign the spot nearest to the car .


Notification System -> In case of busy Days user can subscribe to the parking lot and gets notification if any spot gets available . We can achieve this with the help of observer design pattern.