This document explains how the Pizza Restaurant System adheres to the SOLID principles using various design patterns.
The Single Responsibility Principle states that a class should have only one reason to change. Each class should handle a single, well-defined functionality.
- InventoryManager (Singleton Pattern): Manages inventory updates and ensures consistent stock tracking.
- PizzaFactory (Factory Pattern): Focuses solely on pizza creation logic.
- ToppingDecorator (Decorator Pattern): Handles the dynamic addition of toppings to pizzas.
- PaymentMethod (Strategy Pattern): Manages payment logic independently.
The PizzaFactory
class is responsible only for creating pizza objects:
class PizzaFactory:
@staticmethod
def create_pizza(pizza_type):
if pizza_type == "Margherita":
return Margherita()
elif pizza_type == "Pepperoni":
return Pepperoni()
Benefit: Each class has a clear purpose, making the code easier to maintain and extend.
The Open/Closed Principle states that classes should be open for extension but closed for modification. New functionality should be added without altering existing code.
- Decorator Pattern: Allows the addition of new toppings dynamically without modifying the existing pizza classes.
Adding a new topping (e.g., Bacon) can be achieved by creating a new decorator class:
class Bacon(ToppingDecorator):
def get_description(self):
return f"{self.pizza.get_description()}, Bacon"
def get_cost(self):
return self.pizza.get_cost() + 40.0
Benefit: Existing pizza and topping classes remain unchanged.
The Liskov Substitution Principle states that objects of a superclass should be replaceable with objects of a subclass without affecting the functionality.
- All toppings inherit from the
ToppingDecorator
class, which itself inherits from the basePizza
class. This ensures that toppings behave like pizzas.
A Cheese
topping can be used wherever a Pizza
object is expected:
pizza = Margherita()
pizza = Cheese(pizza)
print(pizza.get_description()) # "Margherita Pizza, Cheese"
Benefit: Ensures consistent behavior across all subclasses.
The Interface Segregation Principle states that no client should be forced to depend on methods it does not use. Interfaces should be small and specific.
- The
PaymentMethod
interface is simple and specific, containing only thepay
method.
Each payment method implements the PaymentMethod
interface:
class PaymentMethod(ABC):
@abstractmethod
def pay(self, amount):
pass
class Cash(PaymentMethod):
def pay(self, amount):
print(f"Paid {amount:.2f} EGP in cash.")
Benefit: Each payment method handles only its own logic without being burdened by unnecessary methods.
The Dependency Inversion Principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.
- The
main
function depends on thePaymentMethod
abstraction rather than concrete payment classes likeCash
orOrangeCash
.
The payment logic uses the abstract PaymentMethod
interface:
payment_method = Cash()
payment_method.pay(total_cost)
Benefit: New payment methods can be added without changing the main
function.
By adhering to the SOLID principles, the Pizza Restaurant System is modular, maintainable, and extensible, ensuring it meets current requirements while being adaptable to future changes.