Requirements

  1. Parking spot assignment based on vehicle size
  2. Entry/Exit ticket management
  3. Calculate parking fees based on duration
  4. Keep track of available and occupied spots


Define Core Objects

  1. Vehicle
  2. Spot
  3. Floor
  4. Garage
  5. Parking System



Analyze Relationships

Certainly! Here's a brief analysis of the relationships between your core objects:

  1. Garage to Floor:
    • Relationship: The Garage contains multiple Floors.
    • Type: Composition (each Floor is a part of the Garage).
  2. Floor to Spot:
    • Relationship: Each Floor manages multiple Spots.
    • Type: Aggregation (Floors contain multiple parking Spots).
  3. Spot to Vehicle:
    • Relationship: A Spot can be occupied by a Vehicle.
    • Type: Association (A Spot may hold a Vehicle, but the Vehicle is not a permanent part of the Spot).
  4. Parking System to Garage:
    • Relationship: Parking System controls and oversees the Garage.
    • Type: Aggregation or Management (The Parking System uses the Garage to perform operations).
  5. Vehicle to Parking System:
    • Relationship: Vehicles interact with the Parking System to be assigned a Spot or to exit.
    • Type: Interaction (The Parking System facilitates the coordination of Vehicle parking).



Establish Hierarchy

  1. Vehicle:
    • Can have subclasses like Car, Motorcycle, Truck for different vehicle types.
  2. Spot:
    • Could have subclasses based on size (e.g., SmallSpot, MediumSpot, LargeSpot).
  3. Floor:
    • Composed of multiple Spot instances.
  4. Garage:
    • Composed of multiple Floor instances.
  5. Parking System:
    • Manages and interacts with the Garage and handles overall operations.




Design Patterns

  1. Factory Pattern:
    • Used for creating objects like ParkingSpots of different types (small, medium, large) based on the vehicle size required.
  2. Strategy Pattern:
    • Used for implementing various pricing strategies (e.g., Hourly, Daily, Event-based).
  3. Singleton Pattern:
    • Ensures that there is only one instance of the Parking System or Garage controller managing the operations.
  4. Observer Pattern (optional for future enhancements):
    • Could be used to update display boards with real-time spot availability changes.



import datetime import math from enum import Enum class Vehicle: def __init__(self, size): self.size = size class Driver: def __init__(self, id, vehicle): self.id = id self.vehicle = vehicle self.payment_due = 0 def charge(self, amount): self.payment_due += amount class Size(Enum): SMALL = 1 MEDIUM = 2 LARGE = 3 class Car(Vehicle): def __init__(self): super().__init__(Size.SMALL) class Limo(Vehicle): def __init__(self): super().__init__(Size.MEDIUM) class SemiTruck(Vehicle): def __init__(self): super().__init__(Size.LARGE) class Spot: def __init__(self, id, size): self.size = size self.id = id self.vehicle = None def assign_vehicle(self, vehicle): self.vehicle = vehicle def release_vehicle(self): vehicle = self.vehicle self.vehicle = None return vehicle class Floor: def __init__(self, small, medium, large): self.spots = [] for i in range(small): self.spots.append(Spot(f'Small-{i+1}', Size.SMALL)) for i in range(medium): self.spots.append(Spot(f'Medium-{i+1}', Size.MEDIUM)) for i in range(large): self.spots.append(Spot(f'Large-{i+1}', Size.LARGE)) self.vehicle_map = {} # vehicle: spot ID def find_available_spot(self, vehicle): for spot in self.spots: if not spot.vehicle and spot.size.value >= vehicle.size.value: return spot return None def assign_vehicle_to_spot(self, vehicle): available_spot = self.find_available_spot(vehicle) if available_spot: available_spot.assign_vehicle(vehicle) self.vehicle_map[vehicle] = available_spot return True return False def release_spot(self, id): for spot in self.spots: if spot.spot_id == spot_id: vehicle = spot.release_vehicle() del self.vehicle_map[vehicle] return True return False class Garage: def __init__(self): # [] self.floors = [] def addFloor(self, small, medium, large): self.floors.append(Floor(small, medium, large)) def park_vehicle(self, vehicle): for floor in self.floors: if floor.assign_vehicle_to_spot(vehicle): return True return False def remove_vehicle(self, vehicle): for floor in self.floors: if vehicle in floor.vehicle_map: floor.release_spot(self.vehicle_map[vehicle]) return True return False class ParkingSystem: def __init__(self, parkingGarage, hourlyRate): self.parkingGarage = parkingGarage self.hourlyRate = hourlyRate self.timeParked = {} # map driverId to time that they parked def park_vehicle(self, driver): isParked = self.parkingGarage.park_vehicle(driver.get_vehicle()) if isParked: self.timeParked[driver.id] = datetime.datetime.now() return isParked def remove_vehicle(self, driver): if driver.id not in self.timeParked: return False currentHour = datetime.datetime.now().hour time_parked = self.timeParked[driver.id] duration = datetime.datetime.now() - time_parked hours_billed = math.ceil(duration.total_seconds() / 3600) driver.charge(hours_billed * self.hourlyRate) del self.timeParked[driver.id] return self.parkingGarage.remove_vehicle(driver.id)




Adhere to SOLID Guidelines

  1. Single Responsibility Principle (SRP):
    • ParkingSpot and ParkingFloor handle specific and distinct tasks: ParkingSpot manages individual spot state, ParkingFloor manages the collection of spots. This separation of concerns aligns with SRP as each class is responsible for a specific part of the system.
  2. Open/Closed Principle (OCP):
    • You can extend your system to include new types of spots or floors without changing the existing classes. For instance, introducing a new spot type can be done by creating a new class while leaving existing components unchanged.
  3. Liskov Substitution Principle (LSP):
    • You can replace different types of ParkingSpot (Small, Medium, Large) without affecting the operations of the ParkingFloor. Each spot type adheres to the same interface or base class behavior.
  4. Interface Segregation Principle (ISP):
    • Although not explicitly shown, by designing small, focused classes like ParkingSpot and ParkingFloor that do not include unnecessary methods, you follow ISP. You can further enhance this by defining detailed interfaces if your system complexity increases.
  5. Dependency Inversion Principle (DIP):
    • If your system uses a strategy for assigning vehicles to spots based on type, then high-level modules (like ParkingFloor operations) rely on abstractions (e.g., a Spot Finder Interface) rather than specific logic implemented inside the floor logic.






Consider Scalability and Flexibility

Scalability

  1. Additional Floors and Spots:
    • By using a ParkingFloor class that can be instantiated multiple times within a ParkingLot, you can easily scale the number of floors. Each floor can be initialized with varying numbers and types of ParkingSpots, allowing you to adjust the capacity as needed.
  2. Flexible Spot Allocation:
    • With individual ParkingSpot management, adding more spots or configuring different sizes/types is straightforward. You simply update the initialization parameters to reflect the desired count and types of spots per floor.
  3. Tracking and Operations:
    • You maintain a collection of ParkingSpot objects, allowing for efficient tracking and operations such as finding available spots and updating spot states. This helps to ensure quick operations even as more spots and vehicles are added.

Extensibility

  1. Adding New Vehicle Types:
    • If you decide to introduce new vehicle types, you can extend the existing Vehicle class or introduce new subclasses, such as Van or ElectricVehicle. This approach allows easy integration without altering the existing system structure significantly.
  2. Enhanced Pricing Models:
    • You can implement additional PricingStrategy classes to introduce new pricing models (e.g., weekend rates, progressive pricing, etc.). The system can then switch between strategies without needing to modify the core logic.
  3. Reservation System:
    • To implement a reservation system, you could add a reservation status to each ParkingSpot and maintain a list of upcoming reservations. This feature would involve extending your ParkingSpot management with methods to reserve and track reservations without changing the existing core functionality.
  4. Advanced Spot Allocation:
    • You can overlay more complex logic for spot allocation (e.g., priority for electric vehicles with charging stations) by adding algorithms to the ParkingFloor class. This can be achieved by refining findAvailableSpot() methods to incorporate new rules.
  5. Multi-lot Management:
    • If your organization manages multiple parking lots, a ParkingLotManager class could be introduced to handle collections of ParkingLot instances. This allows the system to scale to manage multiple facilities from a central framework.






Create/Explain your diagram(s)

Diagram Explanation

  • ParkingLot: Acts as a central manager. It adds floors, finds available spots, issues tickets, and processes exits.
  • ParkingFloor: Contains multiple ParkingSpot instances. It's responsible for finding and assigning spots.
  • ParkingSpot: Represents each physical spot with methods to assign or release vehicles.
  • Vehicle: Base class for vehicles with a method to get the vehicle size. Subclasses like Car, Motorcycle, and Truck inherit from it.
  • ParkingTicket: Tracks parking session details, such as marking an exit.
  • PricingStrategy: Interface for pricing calculations, allowing different implementations like Hourly or Daily pricing.

Relationships

  • Composition: ParkingLot contains multiple ParkingFloor instances, and ParkingFloor contains ParkingSpots.
  • Association: Vehicles occupy ParkingSpots, and ParkingTickets are associated with vehicle parking sessions.
  • Inheritance: PricingStrategy is an interface implemented by different pricing models (like HourlyPricingStrategy).






Future improvements

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