Requirements
- Parking spot assignment based on vehicle size
- Entry/Exit ticket management
- Calculate parking fees based on duration
- Keep track of available and occupied spots
Define Core Objects
- Vehicle
- Spot
- Floor
- Garage
- Parking System
Analyze Relationships
Certainly! Here's a brief analysis of the relationships between your core objects:
- Garage to Floor:
- Relationship: The Garage contains multiple Floors.
- Type: Composition (each Floor is a part of the Garage).
- Floor to Spot:
- Relationship: Each Floor manages multiple Spots.
- Type: Aggregation (Floors contain multiple parking Spots).
- 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).
- 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).
- 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
- Vehicle:
- Can have subclasses like Car, Motorcycle, Truck for different vehicle types.
- Spot:
- Could have subclasses based on size (e.g., SmallSpot, MediumSpot, LargeSpot).
- Floor:
- Composed of multiple Spot instances.
- Garage:
- Composed of multiple Floor instances.
- Parking System:
- Manages and interacts with the Garage and handles overall operations.
Design Patterns
- Factory Pattern:
- Used for creating objects like ParkingSpots of different types (small, medium, large) based on the vehicle size required.
- Strategy Pattern:
- Used for implementing various pricing strategies (e.g., Hourly, Daily, Event-based).
- Singleton Pattern:
- Ensures that there is only one instance of the Parking System or Garage controller managing the operations.
- 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
- 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.
- 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.
- 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.
- 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.
- 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
- Additional Floors and Spots:
- By using a
ParkingFloorclass that can be instantiated multiple times within aParkingLot, you can easily scale the number of floors. Each floor can be initialized with varying numbers and types ofParkingSpots, allowing you to adjust the capacity as needed.
- By using a
- Flexible Spot Allocation:
- With individual
ParkingSpotmanagement, 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.
- With individual
- Tracking and Operations:
- You maintain a collection of
ParkingSpotobjects, 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.
- You maintain a collection of
Extensibility
- Adding New Vehicle Types:
- If you decide to introduce new vehicle types, you can extend the existing
Vehicleclass or introduce new subclasses, such asVanorElectricVehicle. This approach allows easy integration without altering the existing system structure significantly.
- If you decide to introduce new vehicle types, you can extend the existing
- Enhanced Pricing Models:
- You can implement additional
PricingStrategyclasses 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.
- You can implement additional
- Reservation System:
- To implement a reservation system, you could add a reservation status to each
ParkingSpotand maintain a list of upcoming reservations. This feature would involve extending yourParkingSpotmanagement with methods to reserve and track reservations without changing the existing core functionality.
- To implement a reservation system, you could add a reservation status to each
- 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
ParkingFloorclass. This can be achieved by refiningfindAvailableSpot()methods to incorporate new rules.
- You can overlay more complex logic for spot allocation (e.g., priority for electric vehicles with charging stations) by adding algorithms to the
- Multi-lot Management:
- If your organization manages multiple parking lots, a
ParkingLotManagerclass could be introduced to handle collections ofParkingLotinstances. This allows the system to scale to manage multiple facilities from a central framework.
- If your organization manages multiple parking lots, a
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
ParkingSpotinstances. 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...