Requirements

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

1) Include multiple level of building floors which means having including multiple list inside a list in Java

2) For each list in Java or building floor, we define a class VehicleData which includes vehicle size - Small, Medium and Big and availability of space for medium and big vehicles and parked duration of the vehicle. This List Data type will of this class type VehicleData




Define Core Objects

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

1) Parking Lot Class contains

a) ParkingFloor Class - contains methods for available floor, add floor and allot vehicle size parking quantity for each floor

b) Vehicle Data - contains method for checking vehicle size - small, medium and large and track global quantity of vehicle for each size

c) ParkingSpot Class - contains methods for each floor, we check available spot for Small and Large Size vehicles and allot or not allot base on availability. If we allot, we catch the timestamp and add the quantity of vehicle size and decrease the allotment quantity

d) Parking Fee Calculator - We check for each vehicle how much time we charge for Small, medium and large size vehicles based on time. Time will be current timestamp - timestamp when the vehicle was parked




Analyze Relationships

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

  1. Composition: Parking Lot contains Parking Floor
  2. Aggregation: ParkingFloor contains Parking Spot
  3. Association: ParkingSpot contains VehicleData
  4. Dependencies: ParkingFeeCalculator depends on VehicleData
  5. Inheritance: We may built a base class Vehicle and create class like electric cars, SUV based on Vehicle




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...

Vehicle Class

  • String licenseNumber
  • String vehicleType
  • DateTime arrivalTime
  • park
  • unpark

Car Inherit vehicle class

  • carType

Truck inherits vehicle class

  • loadCapactiy

Electric vehicle inherits vehicle class

  • int batteryLevel




Design Patterns

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

  • Singleton Pattern: Parking lot
  • Factory pattern: create instances of different vehicle types
  • Observer pattern: ParkingSpot status updates




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.


public class Car { ... } // Example



1. Vehicle Class (Base Class)

public abstract class Vehicle {

  private String licenseNumber;

  private String vehicleType; // e.g. Car, Truck, Electric Vehicle

  private LocalDateTime arrivalTime;


  public Vehicle(String licenseNumber, String vehicleType) {

    this.licenseNumber = licenseNumber;

    this.vehicleType = vehicleType;

    this.arrivalTime = LocalDateTime.now();

  }


  public String getLicenseNumber() {

    return licenseNumber;

  }


  public String getVehicleType() {

    return vehicleType;

}


  public LocalDateTime getArrivalTime() {

    return arrivalTime;

  }


  public abstract void park();

  public abstract void unpark();

}

2. Car Class (Inherits from Vehicle)

public class Car extends Vehicle {

  private String carType; // e.g. sedan, convertible


  public Car(String licenseNumber, String carType) {

    super(licenseNumber, "Car");

    this.carType = carType;

  }


  public String getCarType() {

    return carType;

  }


  @Override

  public void park() {

    // specific parking logic for Cars

  }


  @Override

  public void unpark() {

    // specific un-parking logic for Cars

  }

}


3. Truck Class (Inherits from Vehicle)

public class Truck extends Vehicle {

  private float loadCapacity; // in tons


  public Truck(String licenseNumber, float loadCapacity) {

    super(licenseNumber, "Truck");

    this.loadCapacity = loadCapacity;

  }


  public float getLoadCapacity() {

    return loadCapacity;

  }


  @Override

  public void park() {

    // specific parking logic for Trucks

  }


  @Override

  public void unpark() {

    // specific un-parking logic for Trucks

  }

}

4. ElectricVehicle Class (Inherits from Vehicle)

public class ElectricVehicle extends Vehicle {

  private int batteryLevel; // in percentage


  public ElectricVehicle(String licenseNumber, int batteryLevel) {

    super(licenseNumber, "Electric Vehicle");

    this.batteryLevel = batteryLevel;

  }


  public int getBatteryLevel() {

    return batteryLevel;

  }


  public void charge() {

    // Logic to charge the vehicle's battery

  }


  @Override

  public void park() {

    // specific parking logic for Electric Vehicles

  }


  @Override

  public void unpark() {

    // specific un-parking logic for Electric Vehicles

  }

}

5. ParkingSpot Class

import java.util.ArrayList;

import java.util.List;


public class ParkingSpot {

  private int spotNumber;

  private boolean isAvailable;

  private Vehicle currentVehicle;


  public ParkingSpot(int spotNumber) {

    this.spotNumber = spotNumber;

    this.isAvailable = true;

    this.currentVehicle = null;

  }


  public boolean isAvailable() {

    return isAvailable;

  }


  public void allocateVehicle(Vehicle vehicle) {

    this.currentVehicle = vehicle;

    this.isAvailable = false;

  }


  public Vehicle removeVehicle() {

    Vehicle vehicle = this.currentVehicle;

    this.currentVehicle = null;

    this.isAvailable = true;

    return vehicle;

  }


  public int getSpotNumber() {

    return spotNumber;

  }

}

6. ParkingLot Class

import java.util.ArrayList;

import java.util.List;


public class ParkingLot {

  private List parkingSpots;


  public ParkingLot(int numberOfSpots) {

    parkingSpots = new ArrayList<>(numberOfSpots);

    for (int i = 0; i < numberOfSpots; i++) {

      parkingSpots.add(new ParkingSpot(i + 1));

    }

  }


  public ParkingSpot findAvailableSpot() {

    for (ParkingSpot spot : parkingSpots) {

      if (spot.isAvailable()) {

        return spot;

      }

    }

    return null; // No available spots

  }


  public void parkVehicle(Vehicle vehicle) {

    ParkingSpot spot = findAvailableSpot();

    if (spot != null) {

      spot.allocateVehicle(vehicle);

      vehicle.park(); // Call specific parking logic for the vehicle

    } else {

      System.out.println("No available parking spots!");

    }

  }


  public void unparkVehicle(ParkingSpot spot) {

    Vehicle vehicle = spot.removeVehicle();

    vehicle.unpark(); // Call specific un-parking logic for the vehicle

  }

}



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.)...

  1. Single Responsibility Principle (SRP)

ParkingFeeCalculator ParkingFeeCalculator class should only be responsible for calculating fees.


public class ParkingFeeCalculator { public double calculateFee(Vehicle vehicle, Duration parkedDuration) { // Calculation logic based on vehicle size and duration }}


2.Open/Closed Principle (OCP):

  • If you want to introduce new vehicle types (e.g., ElectricVehicle), you can do this by creating new subclasses without modifying existing classes like Vehicle. This allows expansion without risk of breaking existing functionality.

public class ElectricVehicle extends Vehicle { private int batteryLevel; // Additional methods specific to electric vehicles}


3.Liskov Substitution Principle (LSP):

  • When a method expects a Vehicle type, it should be able to accept any subclass (like Car or Truck) without any undesired side effects. For instance, using a Truck object where a Vehicle is expected should not cause issues.

4.Interface Segregation Principle (ISP):

  • Definition: Clients should not be forced to depend on interfaces they do not use.
  • Example:
    • Instead of having one large Vehicle interface with all possible methods (for all vehicle types), create smaller, specific interfaces, such as Chargeable for electric vehicles, which contains only methods related to charging.

public interface Chargeable { void charge();};}


5. Dependency Inversion Principle (DIP):

  • Definition: High-level modules should not depend on low-level modules. Both should depend on abstractions (interfaces).
  • Example:
    • In your system, instead of directly depending on specific vehicle implementations (Car, Truck), a class that manages parking should rely on a Vehicle interface. This decouples the code and enables easier changes or enhancements.


public class ParkingManager { public void parkVehicle(Vehicle vehicle) { // Logic to park the vehicle }}



Consider Scalability and Flexibility

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


  1. Vehicle Management:
    • Design the Vehicle class hierarchy to easily incorporate new types of vehicles in the future without modifying existing code. This allows easy extension of functionalities while maintaining compatibility with existing services.
  2. Parking Fee Calculation:
    • Implement a flexible fee structure using various strategies for different vehicle types, making it easy to adapt to changes without a complete system overhaul.
  3. Distributed Architecture:
    • If your parking lot management system gains popularity, consider a cloud-based solution that supports scaling both horizontally and vertically. This could involve using distributed databases or Microservices, where different services (e.g., vehicle registration, fee calculation, reporting) can be scaled independently.
  4. Event-Driven Architecture:
    • Implementing an event-driven model (e.g., using message queues) may facilitate updates across different parts of the system (like notifications about free spots) without coupling components too tightly.

Example of Scalability Consideration:

Imagine your parking lot expands to multiple locations. You would need the system to manage multiple floors and hundreds of parking spots across various sites. Designing your classes in a way that they can be instantiated for different locations without significant code changes would be crucial. For instance, if each parking lot requires operating independently, you can create a ParkingLot manager that dynamically loads each instance based on configuration.


public class ParkingLotManager { private List<ParkingLot> parkingLots; public ParkingLotManager() { this.parkingLots = new ArrayList<>(); // Load parking lots dynamically from configuration } public void addParkingLot(ParkingLot newLot) { parkingLots.add(newLot); } public void parkVehicleInLot(String lotId, Vehicle vehicle) { ParkingLot lot = findParkingLot(lotId); if (lot != null) { lot.parkVehicle(vehicle); } } // More methods for managing multiple parking lots...}





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...


classDiagram class Vehicle { +String licenseNumber +String vehicleType +LocalDateTime arrivalTime +park() void +unpark() void } class Car { +String carType } class Truck { +float loadCapacity } class Truck { +float loadCapacity } class ElectricVehicle { +int batteryLevel +charge() void } class ParkingSpot { +int spotNumber +isAvailable() boolean +allocateVehicle(Vehicle vehicle) void +removeVehicle() Vehicle }class ParkingLot { +List<ParkingSpot> parkingSpots +findAvailableSpot() ParkingSpot +parkVehicle(Vehicle vehicle) void } class ParkingFeeCalculator { +calculateFee(Vehicle vehicle, Duration parkedDuration) double } Vehicle <|-- Car : Inherits Vehicle <|-- Truck : Inherits Vehicle <|-- ElectricVehicle : Inherits ParkingLot "1" o-- "*" ParkingSpot : contains ParkingLot --> ParkingFeeCalculator : uses



Future improvements

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

1. Mobile Application Integration

  • Enhancement: Develop a mobile app that allows users to find available parking spots, reserve them in advance, and make payments.
  • Benefit: Improves user convenience and could enhance occupancy rates by allowing pre-booking.

2. Dynamic Pricing Model

  • Enhancement: Implement a dynamic pricing structure that adjusts fees based on demand, time of day, or special events.
  • Benefit: This could maximize revenue for the parking lot and better allocate resources based on demand.

3. Real-time Availability Updates

  • Enhancement: Use IoT sensors to provide real-time data on space availability and status updates to both users and the display board.
  • Benefit: Customers can make informed decisions about where to park, and better use of space can lead to reduced congestion.

4. Data Analytics and Reporting

  • Enhancement: Implement tools to analyze usage patterns, peak usage times, and customer behavior.
  • Benefit: Insights derived can guide future pricing strategies, marketing efforts, and operational optimizations.

5. Integration with Navigation Apps

  • Enhancement: Collaborate with navigation applications (like Google Maps or Waze) to show real-time parking availability to drivers as they approach the area.
  • Benefit: Helps attract more users by ensuring they know parking is available when they arrive.

6. Support for Electric Vehicle Charging

  • Enhancement: Install charging stations for electric vehicles, allowing them to charge while parked.
  • Benefit: Attracts more electric vehicle users and keeps your system up-to-date with emerging technologies.


7. Automated Payment Solutions

  • Enhancement: Integrate automated payment systems, such as mobile wallets or QR code scanning for contactless payments.
  • Benefit: Streamlines the payment process, reducing time spent at kiosks or in queues.

8. Enhanced User Authentication and Security

  • Enhancement: Implement an authentication mechanism for user profiles and transactions, including features like biometrics or two-factor authentication.
  • Benefit: Enhances security, protecting user data and payment information.