Claude Artifact

The Open-Closed Principle (OCP) is a core principle from SOLID that states "software entities should be open for extension but closed for modification." Let me demonstrate this with a clear example:

# Bad Example - Violates Open-Closed Principle
class DiscountCalculator:
    def calculate_discount(self, product_type: str, price: float) -> float:
        if product_type == "electronics":
            return price * 0.9  # 10% discount
        elif product_type == "clothing":
            return price * 0.85  # 15% discount
        elif product_type == "furniture":
            return price * 0.75  # 25% discount
        # Adding a new product type requires modifying existing code
        return price

# Good Example - Follows Open-Closed Principle
from abc import ABC, abstractmethod

class DiscountStrategy(ABC):
    @abstractmethod
    def calculate_discount(self, price: float) -> float:
        pass

class ElectronicsDiscount(DiscountStrategy):
    def calculate_discount(self, price: float) -> float:
        return price * 0.9  # 10% discount

class ClothingDiscount(DiscountStrategy):
    def calculate_discount(self, price: float) -> float:
        return price * 0.85  # 15% discount

class FurnitureDiscount(DiscountStrategy):
    def calculate_discount(self, price: float) -> float:
        return price * 0.75  # 25% discount

# Adding a new discount type without modifying existing code
class BookDiscount(DiscountStrategy):
    def calculate_discount(self, price: float) -> float:
        return price * 0.95  # 5% discount

class Product:
    def __init__(self, name: str, price: float, discount_strategy: DiscountStrategy):
        self.name = name
        self.price = price
        self.discount_strategy = discount_strategy

    def get_discounted_price(self) -> float:
        return self.discount_strategy.calculate_discount(self.price)

# Example usage
def main():
    # Create products with different discount strategies
    laptop = Product("Laptop", 1000.0, ElectronicsDiscount())
    shirt = Product("Shirt", 50.0, ClothingDiscount())
    chair = Product("Chair", 200.0, FurnitureDiscount())
    book = Product("Book", 30.0, BookDiscount())

    # Calculate discounted prices
    products = [laptop, shirt, chair, book]

    for product in products:
        original_price = product.price
        discounted_price = product.get_discounted_price()
        discount_percentage = ((original_price - discounted_price) / original_price) * 100

        print(f"{product.name}:")
        print(f"  Original price: ${original_price:.2f}")
        print(f"  Discounted price: ${discounted_price:.2f}")
        print(f"  Discount applied: {discount_percentage:.1f}%\\\\n")

# Custom Holiday Discount - Adding new functionality without modifying existing code
class HolidayDiscount(DiscountStrategy):
    def __init__(self, extra_discount: float):
        self.extra_discount = extra_discount

    def calculate_discount(self, price: float) -> float:
        return price * (1 - self.extra_discount)

if __name__ == "__main__":
    main()

    # Using the new holiday discount
    holiday_product = Product("Holiday Special", 100.0, HolidayDiscount(0.3))
    discounted_price = holiday_product.get_discounted_price()
    print(f"Holiday Special:")
    print(f"  Original price: ${100.0:.2f}")
    print(f"  Discounted price: ${discounted_price:.2f}")
    print(f"  Discount applied: {30.0:.1f}%")

Let's break down why the second example follows the Open-Closed Principle:

  1. Closed for Modification:
  2. Open for Extension:
  3. Key Benefits:
  4. Real-World Application: In practice, this pattern is used in many scenarios:

The bad example would require modifying the existing calculate_discount method every time a new product type is added, which:

Would you like me to show:

  1. How to test this OCP-compliant design?
  2. How to combine it with other SOLID principles?
  3. Another real-world example of OCP?