Requirements

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


Primary functions:

  1. Parking spot assignment
    1. Assigns the most suitable parking spot based on car size and spot availability
  2. Spot availability checking
    1. Allows users to check if a spot is available, reserved, or occupied across multiple floors
  3. Fee calculation
    1. Calculates the fee to park based on parking duration


Actors:

  1. Driver
    1. Enters parking lot
    2. Gets spot assigned
    3. Parks and leaves
    4. Pays a fee based on how long they parked




Define Core Objects

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


Objects:

  1. Parking lot
    1. Attributes
      1. List of floors
      2. Overall capacity
  2. Floor
    1. Attributes
      1. Floor id
      2. List of parking spots
      3. Floor capacity
  3. ParkingSpot
    1. Attributes
      1. Spot ID
      2. Size
      3. Status
  4. Vehicle
    1. Attributes
      1. Vehicle ID
      2. Size
  5. Ticket
    1. Attributes
      1. ID of parked vehicle
      2. ID of reserved parking spot
      3. Entry time
      4. Exit time
  6. Fee calculator
    1. Methods
      1. calculateFee(duration)
  7. ParkingLotManager
    1. Methods
      1. create_ticket(vehicle_id, spot_id, entry_time, exit_time)
      2. check_availability(vehicle_id, spot_id, entry_time, exit_time)
      3. compute_fee(calculateFee)




Analyze Relationships

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


  1. Parking lot contains multiple floors
    1. Composition
    2. Each floor is managed by the parking lot
  2. Floor contains multiple parking spots
    1. Composition
    2. Each parking spot is managed by the floor
  3. Vehicle is linked to ticket during parking
    1. Association
    2. A vehicle is assigned to ticket on creation
  4. Ticket is linked to parking spot
    1. Association
    2. Parking spot is available during the duration of the ticket
    3. Parking spot is assigned to ticket on creation
  5. ParkingLotManager uses FeeCalculator to compute the fee
    1. Utilization
    2. The system computes the fee using entry and exit time
  6. ParkingLotManager is linked to tickets
    1. Association
    2. Creates the ticket
  7. ParkingLotManager is linked to parking lot
    1. Association
    2. Manages and checks parking lot availability




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 has a base class with size abstract method

  1. It is used to create different vehicles with different sizes.

Floor has a base class with vehicle type abstract method

  1. It is used to to create different floors with different allowed vehicle types


Design Patterns

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


  1. Factory
    1. Car factory creates different types of vehicles
  2. Singleton
    1. A single instance of ParkingLotManager is created as a single source of truth


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.


class ParkingLot: def __init__(self): self.floors = [] # List[Floor] self.total_capacity = 0 # int def add_floor(self, floor): self.floors.append(floor) self.total_capacity += floor.capacity class Floor: def init(self, floor_id, capacity): self.floor_id = floor_id # int or str self.spots = [] # List[ParkingSpot] self.capacity = capacity # int def add_spot(self, spot): self.spots.append(spot) def get_available_spots(self, vehicle_size): # Return list of available spots that match vehicle size pass class ParkingSpot: def init(self, spot_id, size): self.spot_id = spot_id # str self.size = size # str ("small", "medium", "large") self.status = "available" # "available", "occupied", "reserved" def occupy(self): self.status = "occupied" def free_up(self): self.status = "available" class Vehicle: def init(self, vehicle_id, size): self.vehicle_id = vehicle_id # str self.size = size # str ("small", "medium", "large") class Ticket: def init(self, ticket_id, vehicle_id, spot_id, entry_time): self.ticket_id = ticket_id self.vehicle_id = vehicle_id self.spot_id = spot_id self.entry_time = entry_time # datetime self.exit_time = None # datetime def close_ticket(self, exit_time): self.exit_time = exit_time def get_duration(self): return self.exit_time - self.entry_time class FeeCalculator: def calculate_fee(self, entry_time, exit_time): duration = exit_time - entry_time return (duration.total_seconds() / 3600) * 2 # $2/hour class ParkingLotManager: def init(self, parking_lot): self.parking_lot = parking_lot # ParkingLot self.tickets = {} # Dict[ticket_id: Ticket] def check_availability(self, vehicle): # Iterate through floors and spots to find a match pass def create_ticket(self, vehicle, spot, entry_time): ticket_id = generate_unique_id() ticket = Ticket(ticket_id, vehicle.vehicle_id, spot.spot_id, entry_time) spot.occupy() self.tickets[ticket_id] = ticket return ticket def close_ticket(self, ticket_id, exit_time): ticket = self.tickets.get(ticket_id) ticket.close_ticket(exit_time) fee = FeeCalculator().calculate_fee(ticket.entry_time, exit_time) # Free up the spot # return fee and ticket summary return fee





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. I made FeeCalculator a separate class to follow Single Responsibility — it doesn’t need to know about vehicles or tickets.
  2. The vehicle factory exhibits open/closed principle by not modifying core behavior but extending car types.




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. The system is flexible and scalable due to its use of a factory pattern for vehicle creation. New vehicle types can be added without modifying the core logic of the vehicle class, adhering to the Open/Closed Principle. Additionally, the fee calculation logic follows the Single Responsibility Principle and is loosely coupled, making it easy to extend with new pricing strategies or fee rules without impacting other parts of the system.
  2. The core objects of the system are decoupled from each other, making it easy to scale horizontally. We can then allocate resources only to components that need to be scaled by making them individual services. The interoperability of the system is well defined to ensure no issues in decoupling.






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