Designing a Parking Lot System involves the use of various design patterns to structure the system in a way that is flexible, scalable, and easy to maintain. In this case, the Parking Lot System is built to manage parking spots, vehicles, parking tickets, and payment modes.
Table of Contents
Requirment 1
- Design a basic Parking lot
Solution
Identify key objects/classes in the system.
- Parking Spot: Space for parking
- Attributes:
spot_id
- is_available
- assigned_vehicle
- Attributes:
- Parking Lot : A parking lot has many parking spot
- Attributes
- slots
- Method
- add_spot()
- find_available_spot()
- park_vehicle()
- remove_vehicle()
- Attributes
- Vehicle : Vehicle will park
- Attributes
- license_plate
- Attributes
class Vehicle:
def __init__(self,license_plate):
self.license_plate = license_plate
class ParkingSpot:
def __init__(self,spot_id):
self.spot_id=spot_id
self.is_available = True
self.assigned_vehicle = None
def slot_available(self):
return self.is_available
def book_slot(self,vehicle):
self.is_available = False
self. assigned_vehicle = vehicle.license_plate
def unbook_slot(self):
self.is_available = True
self. assigned_vehicle = None
class ParkingLot:
def __init__(self):
self.slots = []
def add_spot(self,spot):
self.slots.append(spot)
def find_available_spot(self):
for slot in self.slots:
if slot.slot_available():
return slot
return None
def park_vehicle(self,vehicle):
slot = self.find_available_spot()
if slot:
slot.book_slot(vehicle)
print(f"Vehicle {vehicle.license_plate} Parked at {slot.spot_id}")
return slot.spot_id
print("No Space")
return None
def remove_vehicle(self,vehicle):
for slot in self.slots:
if slot.license_plate ==vehicle.license_plate:
slot.unbook_slot()
print(f"Vehicle {vehicle.license_plate} unparked from {slot.spot_id}")
# Vehicles
car1 = Vehicle("MH1234")
car2 = Vehicle("DL987")
car3 = Vehicle("KA456")
#Adding Parking Spot in parking lot
slot = ParkingSpot("a1")
lot = ParkingLot()
lot.add_spot(slot)
#Parking
lot.park_vehicle(car1)
lot.park_vehicle(car2)
lot.remove_vehicle(car1)
lot.park_vehicle(car2)
lot.park_vehicle(car3)
'''
Output
Vehicle MH1234 Parked at a1
No Space
Vehicle MH1234 unparked from a1
Vehicle DL987 Parked at a1
No Space
'''
Python+----------------+ +----------------+ +------------------+
| ParkingLot |<>---------| ParkingSpot |<>---------| Vehicle |
+----------------+ +----------------+ +------------------+
| - name: str | | - spot_id: str | | - license_plate: str |
| - spots: list | | - spot_type: str +------------------+
+----------------+ | - is_free: bool|
| +add_spot() | | - assigned_vehicle: Vehicle|
| +find_spot() | +----------------+
| +park_vehicle()| | +park_vehicle()|
| +remove_vehicle()| | +remove_vehicle()|
+----------------+ +----------------+
PythonKey Points
Use getter/setter methods, instead of directly accessing variables like slot.license_plate.
if slot.license_plate == vehicle.license_plate: # bad Practice
if slot.license_plate() == vehicle.license_plate(): #Good
PythonUse str() for better printing. Example In Vehicle Class
def __str__(self):
return f"Vehicle({self.license_plate})"
PythonNow print(vehicle) will show Vehicle(MH1234).
Requirment 2
- Support for various type of vehicle
Solution
To support different type of vehicle, we must have different type of parking spot as well
Identify key objects/classes in the system.
Vehicle : Vehicle will park
- Attributes
- license_plate
One class for each vehicle type
ParkingSpot
- Attributes
- parking_type
class Vehicle:
def __init__(self,license_plate,vehicle_type):
self.license_plate = license_plate
self.vehicle_type = vehicle_type
def get_license_plate(self):
return self.license_plate
def get_vehicle_type(self):
return self.vehicle_type
def __str__(self):
return f"{self.vehicle_type} - {self.license_plate}"
class CarVehicle(Vehicle):
def __init__(self, license_plate):
super().__init__(license_plate, "car")
class BikeVehicle(Vehicle):
def __init__(self, license_plate):
super().__init__(license_plate, "bike")
class ParkingSpot:
def __init__(self,spot_id,parking_type):
self.spot_id=spot_id
self.is_available = True
self.assigned_vehicle = None
self.parking_type = parking_type
def get_parking_type(self):
return self.parking_type
def get_license_plate(self):
return self.assigned_vehicle
def get_spot_id(self):
return self.spot_id
def slot_available(self):
return self.is_available
def book_slot(self,vehicle):
self.is_available = False
self.assigned_vehicle = vehicle.get_license_plate()
def unbook_slot(self):
self.is_available = True
self. assigned_vehicle = None
class ParkingLot:
def __init__(self):
self.slots = []
def add_spot(self,spot):
self.slots.append(spot)
def find_available_spot(self,vehicle_type):
for slot in self.slots:
if slot.slot_available() and slot.get_parking_type() == vehicle_type:
return slot
return None
def park_vehicle(self,vehicle):
slot = self.find_available_spot(vehicle.get_vehicle_type())
if slot:
slot.book_slot(vehicle)
print(f"{vehicle} Parked at {slot.get_spot_id()}")
return slot.get_spot_id()
print(f"No Space for {vehicle}")
return None
def remove_vehicle(self,vehicle):
for slot in self.slots:
if slot.get_license_plate() == vehicle.get_license_plate():
slot.unbook_slot()
print(f"{vehicle} unparked from {slot.get_spot_id()}")
car1 = CarVehicle("MH1234")
car2 = CarVehicle("DL987")
car3 = CarVehicle("UP563")
bike1 = BikeVehicle("KA456")
bike2 = BikeVehicle("TN347")
#Adding Parking Spot in parking lot
slot1 = ParkingSpot("c1","car")
slot2 = ParkingSpot("c2","car")
slot3 = ParkingSpot("b1","bike")
lot = ParkingLot()
lot.add_spot(slot1)
lot.add_spot(slot2)
lot.add_spot(slot3)
#Parking
lot.park_vehicle(car1)
lot.park_vehicle(car2)
lot.remove_vehicle(car1)
lot.park_vehicle(car2)
lot.park_vehicle(bike1)
lot.park_vehicle(bike2)
lot.park_vehicle(car3)
'''
OutPut
car - MH1234 Parked at c1
car - DL987 Parked at c2
car - MH1234 unparked from c1
car - DL987 Parked at c1
bike - KA456 Parked at b1
No Space for bike - TN347
No Space for car - UP563
'''
PythonFuture
- Can add to avoid double parking
- self.assigned_vehicle = vehicle.get_license_plate()
- Which only stores the license plate. This can work, but it’s more powerful if you store the whole vehicle object, so you can access vehicle_type, etc., later if needed.
def book_slot(self, vehicle):
self.is_available = False
self.assigned_vehicle = vehicle # store the object
PythonIf we have multiple values, it wise to pass object only
Factory Method
Problem Without Factory
In any real application, you will get string from user input(or API) like “car” or “bike”
To handle that, we will create the right object based on that string.
if vehicle_type == "car":
vehicle = CarVehicle(license_plate)
elif vehicle_type == "bike":
vehicle = BikeVehicle(license_plate)
PythonThis will work, but now you’re repeating this logic everywhere you need to create a vehicle.
And if you add a new type (like TruckVehicle) — you’ll have to update every place that has this if-elif logic.
What the Factory Pattern Does?
It centralizes that object creation logic into a single class (the Factory).
You just write:
vehicle = VehicleFactory.create_vehicle("car", "MH1234")
PythonAnd the factory decides which class to use internally. If you later add TruckVehicle, you update only the factory — not every part of your codebase.
class VehicleFactory:
@staticmethod
def create_vehicle(vehicle_type, license_plate):
vehicle_type = vehicle_type.lower()
if vehicle_type == "car":
return CarVehicle(license_plate)
elif vehicle_type == "bike":
return BikeVehicle(license_plate)
# Future vehicle types can go here
else:
raise ValueError(f"Unsupported vehicle type: {vehicle_type}")
PythonRequirment 3
- Give Payment Option
Solution
For collecting payment. We need to collection duration the vehicle is parked
Identify key objects/classes in the system.
- Parking Ticket
- Attributes:
- vehicle_plate
- entry_time
- exit_time
- Attributes:
- Payment
- Attributes
- payment_method
- duration
- Attributes
Why NOT to Store Entry/Exit Time in ParkingSpot?
Storing entry/exit time here violates the Single Responsibility Principle (SRP) — because now the spot is handling temporal and transactional logic, which belongs to something like a ParkingTicket.
Per hour rate for different vehicles
We can do it in various different ways
- Maintain a rate card
- For simpicity, I will put in Vehicle subclass
Should self.active_tickets go in the Vehicle class?
No, Because active_tickets is not the responsibility of the vehicle. It’s part of the system’s management of vehicles, not the vehicle itself.
So, active_tickets should go in ParkingLot
from abc import ABC,abstractmethod
from datetime import datetime
class Vehicle:
def __init__(self,license_plate,vehicle_type):
self.license_plate = license_plate
self.vehicle_type = vehicle_type
def get_license_plate(self):
return self.license_plate
def get_vehicle_type(self):
return self.vehicle_type
@abstractmethod
def get_rate_per_hour(self):
pass
def __str__(self):
return f"{self.vehicle_type} - {self.license_plate}"
class CarVehicle(Vehicle):
def __init__(self, license_plate):
super().__init__(license_plate, "car")
def get_rate_per_hour(self):
return 10
class BikeVehicle(Vehicle):
def __init__(self, license_plate):
super().__init__(license_plate, "bike")
def get_rate_per_hour(self):
return 5
class VehicleFactory:
@staticmethod
def create_vehicle(vehicle_type, license_plate):
vehicle_type = vehicle_type.lower()
if vehicle_type == "car":
return CarVehicle(license_plate)
elif vehicle_type == "bike":
return BikeVehicle(license_plate)
# Future vehicle types can go here
else:
raise ValueError(f"Unsupported vehicle type: {vehicle_type}")
class ParkingSpot:
def __init__(self,spot_id,parking_type):
self.spot_id=spot_id
self.is_available = True
self.assigned_vehicle = None
self.parking_type = parking_type
def get_parking_type(self):
return self.parking_type
def get_license_plate(self):
return self.assigned_vehicle
def get_spot_id(self):
return self.spot_id
def slot_available(self):
return self.is_available
def book_slot(self,vehicle):
self.is_available = False
self.assigned_vehicle = vehicle.get_license_plate()
def unbook_slot(self):
self.is_available = True
self. assigned_vehicle = None
class ParkingLot:
def __init__(self):
self.slots = []
self.active_tickets = {} # dict, so that we can reach it by license
def add_spot(self,spot):
self.slots.append(spot)
def find_available_spot(self,vehicle_type):
for slot in self.slots:
if slot.slot_available() and slot.get_parking_type() == vehicle_type:
return slot
return None
def park_vehicle(self,vehicle):
slot = self.find_available_spot(vehicle.get_vehicle_type())
if slot:
slot.book_slot(vehicle)
print(f"{vehicle} Parked at {slot.get_spot_id()}")
ticket = ParkingTicket().get_ticket(vehicle)
self.active_tickets[vehicle.get_license_plate()] = ticket
return slot.get_spot_id()
print(f"No Space for {vehicle}")
return None
def remove_vehicle(self,vehicle):
for slot in self.slots:
if slot.get_license_plate() == vehicle.get_license_plate():
slot.unbook_slot()
ticket = self.active_tickets[vehicle.get_license_plate()]
ticket.mark_exit()
fee = ticket.calculate_fees()
print(f"{vehicle} unparked from {slot.get_spot_id()}, fee Rs{fee}")
del self.active_tickets[vehicle.get_license_plate()]
class ParkingTicket:
def __init__(self):
self.entry_time = None
self.exit_time = None
self.vehicle = None
def get_ticket(self,vehicle):
self.entry_time = datetime.now()
self.vehicle = vehicle
return self
def mark_exit(self):
self.exit_time = datetime.now()
def calculate_fees(self):
duration = self.exit_time - self.entry_time
hours = max(1, duration.total_seconds() // 3600)
rate = self.vehicle.get_rate_per_hour()
return hours * rate
lot = ParkingLot()
# for simplicity, let add spot dynamically
for i in range(5):
lot.add_spot(ParkingSpot(f"c{i}","car"))
for i in range(5):
lot.add_spot(ParkingSpot(f"b{i}","bike"))
car1 = VehicleFactory.create_vehicle("car", "MH1234")
car2 = VehicleFactory.create_vehicle("car", "DL987")
car3 = VehicleFactory.create_vehicle("car", "UP563")
bike1 = VehicleFactory.create_vehicle("bike", "KA456")
bike2 = VehicleFactory.create_vehicle("bike", "TN347")
#Parking
lot.park_vehicle(car1)
lot.park_vehicle(car2)
lot.remove_vehicle(car1)
lot.park_vehicle(car2)
lot.park_vehicle(bike1)
lot.park_vehicle(bike2)
lot.park_vehicle(car3)
lot.remove_vehicle(bike1)
'''
Output
car - MH1234 Parked at c0
car - DL987 Parked at c1
car - MH1234 unparked from c0, fee Rs10
car - DL987 Parked at c0
bike - KA456 Parked at b0
bike - TN347 Parked at b1
car - UP563 Parked at c2
bike - KA456 unparked from b0, fee Rs5
'''
PythonRequirment 4
To support different modes of payment
Identify key objects/classes in the system.
- Payment
- Attributes
- Fee
- Payment time
- Payment status
- Attributes
- ParkingTicket
- Attributes
- make_payment
- Attributes
Why make_payment() is in the ParkingTicket class (not Payment)?
Because, The ParkingTicket knows:
- The entry and exit time
- The vehicle (which has the rate)
- How long the vehicle parked
- Therefore, it can calculate the fee
from abc import ABC,abstractmethod
from datetime import datetime
class Vehicle:
def __init__(self,license_plate,vehicle_type):
self.license_plate = license_plate
self.vehicle_type = vehicle_type
def get_license_plate(self):
return self.license_plate
def get_vehicle_type(self):
return self.vehicle_type
@abstractmethod
def get_rate_per_hour(self):
pass
def __str__(self):
return f"{self.vehicle_type} - {self.license_plate}"
class CarVehicle(Vehicle):
def __init__(self, license_plate):
super().__init__(license_plate, "car")
def get_rate_per_hour(self):
return 10
class BikeVehicle(Vehicle):
def __init__(self, license_plate):
super().__init__(license_plate, "bike")
def get_rate_per_hour(self):
return 5
class VehicleFactory:
@staticmethod
def create_vehicle(vehicle_type, license_plate):
vehicle_type = vehicle_type.lower()
if vehicle_type == "car":
return CarVehicle(license_plate)
elif vehicle_type == "bike":
return BikeVehicle(license_plate)
# Future vehicle types can go here
else:
raise ValueError(f"Unsupported vehicle type: {vehicle_type}")
class ParkingSpot:
def __init__(self,spot_id,parking_type):
self.spot_id=spot_id
self.is_available = True
self.assigned_vehicle = None
self.parking_type = parking_type
def get_parking_type(self):
return self.parking_type
def get_license_plate(self):
return self.assigned_vehicle
def get_spot_id(self):
return self.spot_id
def slot_available(self):
return self.is_available
def book_slot(self,vehicle):
self.is_available = False
self.assigned_vehicle = vehicle.get_license_plate()
def unbook_slot(self):
self.is_available = True
self. assigned_vehicle = None
class ParkingLot:
def __init__(self):
self.slots = []
self.active_tickets = {} # dict, so that we can reach it by license
def add_spot(self,spot):
self.slots.append(spot)
def find_available_spot(self,vehicle_type):
for slot in self.slots:
if slot.slot_available() and slot.get_parking_type() == vehicle_type:
return slot
return None
def park_vehicle(self,vehicle):
slot = self.find_available_spot(vehicle.get_vehicle_type())
if slot:
slot.book_slot(vehicle)
print(f"{vehicle} Parked at {slot.get_spot_id()}")
ticket = ParkingTicket().get_ticket(vehicle)
self.active_tickets[vehicle.get_license_plate()] = ticket
return slot.get_spot_id()
print(f"No Space for {vehicle}")
return None
def remove_vehicle(self,vehicle,payment_mode):
for slot in self.slots:
if slot.get_license_plate() == vehicle.get_license_plate():
slot.unbook_slot()
ticket = self.active_tickets[vehicle.get_license_plate()]
ticket.mark_exit()
ticket.calculate_fees()
print(f"{vehicle} unparked from {slot.get_spot_id()}")
ticket.make_payment(payment_mode)
del self.active_tickets[vehicle.get_license_plate()]
class ParkingTicket:
def __init__(self):
self.entry_time = None
self.exit_time = None
self.vehicle = None
self.fee = 0.0
def get_ticket(self,vehicle):
self.entry_time = datetime.now()
self.vehicle = vehicle
return self
def mark_exit(self):
self.exit_time = datetime.now()
def calculate_fees(self):
duration = self.exit_time - self.entry_time
hours = max(1, duration.total_seconds() // 3600)
rate = self.vehicle.get_rate_per_hour()
self.fee= hours * rate
def make_payment(self, mode):
payment_method = PaymentFactory.get_payment_handler(mode,self.fee)
return payment_method.process()
class Payment(ABC):
def __init__(self,amount):
self.amount = amount
@abstractmethod
def process(self):
pass
class UPIPayment(Payment):
def process(self):
print(f"Processing UPI Payment of {self.amount} with UPI ID")
class CardPayment(Payment):
def process(self):
print(f"Processing Card Payment of {self.amount} with CVV check")
class CashPayment(Payment):
def process(self):
print(f"Accepting cash {self.amount}")
class PaymentFactory:
@staticmethod
def get_payment_handler(mode, amount):
mode = mode.lower()
if mode == "upi":
return UPIPayment(amount)
elif mode == "card":
return CardPayment(amount)
elif mode == "cash":
return CashPayment(amount)
else:
raise ValueError(f"Unsupported payment mode: {mode}")
lot = ParkingLot()
# for simplicity, let add spot dynamically
for i in range(5):
lot.add_spot(ParkingSpot(f"c{i}","car"))
for i in range(5):
lot.add_spot(ParkingSpot(f"b{i}","bike"))
car1 = VehicleFactory.create_vehicle("car", "MH1234")
car2 = VehicleFactory.create_vehicle("car", "DL987")
car3 = VehicleFactory.create_vehicle("car", "UP563")
bike1 = VehicleFactory.create_vehicle("bike", "KA456")
bike2 = VehicleFactory.create_vehicle("bike", "TN347")
#Parking
lot.park_vehicle(car1)
lot.park_vehicle(car2)
lot.remove_vehicle(car1,"upi")
lot.park_vehicle(car2)
lot.park_vehicle(bike1)
lot.park_vehicle(bike2)
lot.park_vehicle(car3)
lot.remove_vehicle(bike1,"cash")
'''
Output
car - MH1234 Parked at c0
car - DL987 Parked at c1
car - MH1234 unparked from c0
Processing UPI Payment of 10 with UPI ID
car - DL987 Parked at c0
bike - KA456 Parked at b0
bike - TN347 Parked at b1
car - UP563 Parked at c2
bike - KA456 unparked from b0
Accepting cash 5
'''
PythonFuture
We add multiple level parking
Design Pattern Used
Factory Method Pattern
Where?
- VehicleFactory.create_vehicle(…)
- PaymentFactory.get_payment_handler(…)
Purpose:
- Provides a way to create objects (e.g., CarVehicle, BikeVehicle, UPIPayment, CashPayment, etc.) without specifying the exact class name directly in your logic.
Why it’s good:
- Promotes scalability: new vehicle or payment types can be added easily.
- Follows Open/Closed Principle: open for extension, closed for modification.
Strategy Pattern
Where?
- Payment modes: UPIPayment, CardPayment, CashPayment (all implement a common process() interface).
Purpose:
- Allows the algorithm (payment processing) to vary independently from the clients using it.
Why it’s cool here:
- You can switch payment mode at runtime without changing the core logic.
Template Method Pattern (minor)
Where?
- Vehicle is an abstract class that defines the interface (get_rate_per_hour) to be implemented by subclasses like CarVehicle, BikeVehicle.
Purpose:
- Defines the skeleton of an algorithm in a base class and lets subclasses override specific steps.
Single Responsibility Principle (SRP)
Not a pattern, but a SOLID principle you are following well.
Examples:
- ParkingLot manages slots and vehicle flow.
- Vehicle handles vehicle details.
- Payment handles payments.
- ParkingTicket manages timing and fees.
Template Pattern Vs Stategy Pattern
Strategy Pattern: The Strategy Pattern is used to define a family of algorithms (or strategies), encapsulate each one, and make them interchangeable.
- The strategy pattern allows the client to choose the appropriate algorithm at runtime without modifying the classes that implement it. This pattern is often used when you have different variants of an algorithm or operation and you want to make them interchangeable.
- The different types of payment (UPI, Card, Cash) implement the common process method in their own way.
- The strategy pattern allows an algorithm (in this case, the payment processing) to be selected at runtime, with each specific implementation encapsulated in a separate class.
class Payment(ABC):
def __init__(self,amount):
self.amount = amount
@abstractmethod
def process(self):
pass
class UPIPayment(Payment):
def process(self):
print(f"Processing UPI Payment of {self.amount} with UPI ID")
class CardPayment(Payment):
def process(self):
print(f"Processing Card Payment of {self.amount} with CVV check")
class CashPayment(Payment):
def process(self):
print(f"Accepting cash {self.amount}")
PythonIn above code, you have different types of payment (UPI, Card, and Cash). Each type of payment has its own distinct implementation for processing a payment. Here’s how the Strategy Pattern works for you:
- The Payment class defines a common interface for all payment types. It includes an abstract method process(), but does not define how the payment should be processed.
- The concrete subclasses (UPIPayment, CardPayment, CashPayment) provide the specific details on how the payment is processed.
The client can select the payment strategy at runtime, like so:
def make_payment(payment: Payment):
payment.process() # Client calls the process method on any Payment type
payment1 = UPIPayment(100)
payment2 = CardPayment(200)
payment3 = CashPayment(300)
make_payment(payment1) # Uses UPI payment logic
make_payment(payment2) # Uses Card payment logic
make_payment(payment3) # Uses Cash payment logic
Pythonclass Payment(ABC):
def __init__(self, amount):
self.amount = amount
def make_payment(self):
self.validate_payment()
self.process()
self.confirm_payment()
def validate_payment(self):
print(f"Validating payment of {self.amount}")
@abstractmethod
def process(self):
pass
def confirm_payment(self):
print(f"Payment of {self.amount} confirmed!")
class UPIPayment(Payment):
def process(self):
print(f"Processing UPI Payment of {self.amount} with UPI ID")
class CardPayment(Payment):
def process(self):
print(f"Processing Card Payment of {self.amount} with CVV check")
PythonLet make it Template
Template Method Pattern
The Template Method Pattern defines the skeleton of an algorithm in a method, deferring some steps to subclasses. The main idea is that the base class provides a template (a fixed structure) for the overall algorithm, while subclasses can provide specific implementations for certain steps in that algorithm.
If you were to use the Template Method Pattern, the base class (Payment) would define a common structure or sequence of steps that every payment needs to go through (e.g., validate the payment, process it, confirm the payment). The concrete classes (UPIPayment, CardPayment, CashPayment) would then only implement the specifics of how each payment type processes the payment.
class Payment(ABC):
def __init__(self, amount):
self.amount = amount
def make_payment(self):
self.validate_payment() # Step 1: Common step
self.process() # Step 2: Specific to subclass
self.confirm_payment() # Step 3: Common step
def validate_payment(self):
print(f"Validating payment of {self.amount}")
@abstractmethod
def process(self):
pass
def confirm_payment(self):
print(f"Payment of {self.amount} confirmed!")
class UPIPayment(Payment):
def process(self):
print(f"Processing UPI Payment of {self.amount} with UPI ID")
class CardPayment(Payment):
def process(self):
print(f"Processing Card Payment of {self.amount} with CVV check")
Python- The make_payment() method in the base class (Payment) defines the overall structure of a payment transaction.
- It first validates the payment (common step).
- Then, it calls the process() method (which will vary by payment type, implemented by subclasses).
- Finally, it confirms the payment (common step).
- The subclasses (UPIPayment, CardPayment) only need to provide the specific details of how the payment is processed.
In this case, you are defining the overall flow of the algorithm in the base class, and only customizing the specific steps (like how the payment is processed) in the subclasses.
Client Code
def make_payment(payment: Payment):
payment.make_payment() # This calls the template method
# Create different payment objects
upi_payment = UPIPayment(100) # UPI payment of 100
card_payment = CardPayment(200) # Card payment of 200
cash_payment = CashPayment(300) # Cash payment of 300
# Process each payment using the common template
make_payment(upi_payment) # Validating payment of 100, Processing UPI Payment of 100 with UPI ID, Payment of 100 confirmed!
make_payment(card_payment) # Validating payment of 200, Processing Card Payment of 200 with CVV check, Payment of 200 confirmed!
make_payment(cash_payment) # Validating payment of 300, Accepting cash 300, Payment of 300 confirmed!
PythonWhich Pattern Should You Use?
Strategy Pattern: Use this pattern if the payment methods (UPI, Card, Cash, etc.) are fundamentally different in how they work, and you don’t need to enforce a strict control flow for how the payment is processed.
Template Method Pattern: Use this pattern if the overall structure of the payment process (e.g., validate, process, confirm) is consistent, but you need different subclasses to implement how each specific step is carried out.
The Strategy Pattern is used when you want to define a family of interchangeable algorithms (e.g., different payment methods), while the Template Method Pattern is used when you want to define a common algorithm structure with customizable steps (e.g., payment processing with a fixed sequence).
Conclusion
The Parking Lot System incorporates various design patterns to manage complexity and enhance flexibility. The Factory Pattern allows for the dynamic creation of different vehicle types (e.g., Car, Bike), while the Strategy Pattern allows different payment methods to be handled seamlessly with a shared process() interface. These patterns ensure that the system can scale to include more vehicle types and payment methods without requiring major code changes. By using these patterns, the design maintains clear separation of concerns, promotes extensibility, and allows the parking lot system to handle diverse operational requirements efficiently. This approach aligns with modern object-oriented design principles, resulting in a robust and adaptable system.
2 thoughts on “LLD Parking Lot”