"""
Open/Closed Principle (OCP)
Software entities (classes, modules, functions) should be open for extension
but closed for modification.
"""
# Bad Example - Violating OCP
class DiscountCalculatorBad:
def calculate_discount(self, product_type, price):
if product_type == "Electronics":
return price * 0.1 # 10% discount for electronics
elif product_type == "Clothing":
return price * 0.2 # 20% discount for clothing
elif product_type == "Furniture":
return price * 0.05 # 5% discount for furniture
# To add a new product type, we have to modify this method
# This violates the Open/Closed Principle
return 0
# Good Example - Following OCP with polymorphism
class DiscountStrategy:
"""Base class for discount strategies"""
def calculate_discount(self, price):
"""Calculate the discount amount for a given price"""
return 0
class ElectronicsDiscountStrategy(DiscountStrategy):
def calculate_discount(self, price):
return price * 0.1 # 10% discount for electronics
class ClothingDiscountStrategy(DiscountStrategy):
def calculate_discount(self, price):
return price * 0.2 # 20% discount for clothing
class FurnitureDiscountStrategy(DiscountStrategy):
def calculate_discount(self, price):
return price * 0.05 # 5% discount for furniture
# If we need to add a new discount strategy, we create a new class
class GroceryDiscountStrategy(DiscountStrategy):
def calculate_discount(self, price):
return price * 0.15 # 15% discount for groceries
class DiscountCalculator:
def __init__(self):
# Map product types to discount strategies
self.discount_strategies = {
"Electronics": ElectronicsDiscountStrategy(),
"Clothing": ClothingDiscountStrategy(),
"Furniture": FurnitureDiscountStrategy(),
"Grocery": GroceryDiscountStrategy()
}
def calculate_discount(self, product_type, price):
# Get the appropriate strategy from the map
strategy = self.discount_strategies.get(product_type, DiscountStrategy())
# Use the strategy to calculate the discount
return strategy.calculate_discount(price)
def add_discount_strategy(self, product_type, strategy):
# We can extend the calculator without modifying existing code
self.discount_strategies[product_type] = strategy
# Client code
if __name__ == "__main__":
price = 1000
print("Bad Example (Violating OCP):")
calc_bad = DiscountCalculatorBad()
electronics_discount = calc_bad.calculate_discount("Electronics", price)
clothing_discount = calc_bad.calculate_discount("Clothing", price)
furniture_discount = calc_bad.calculate_discount("Furniture", price)
print(f"Electronics discount: ${electronics_discount}")
print(f"Clothing discount: ${clothing_discount}")
print(f"Furniture discount: ${furniture_discount}")
print("\nGood Example (Following OCP):")
calc_good = DiscountCalculator()
electronics_discount = calc_good.calculate_discount("Electronics", price)
clothing_discount = calc_good.calculate_discount("Clothing", price)
furniture_discount = calc_good.calculate_discount("Furniture", price)
grocery_discount = calc_good.calculate_discount("Grocery", price)
print(f"Electronics discount: ${electronics_discount}")
print(f"Clothing discount: ${clothing_discount}")
print(f"Furniture discount: ${furniture_discount}")
print(f"Grocery discount: ${grocery_discount}")
# Adding a new discount strategy at runtime
class BooksDiscountStrategy(DiscountStrategy):
def calculate_discount(self, price):
return price * 0.25 # 25% discount for books
# Extend the calculator without modifying its code
calc_good.add_discount_strategy("Books", BooksDiscountStrategy())
books_discount = calc_good.calculate_discount("Books", price)
print(f"Books discount: ${books_discount}")