The before code mixes discount logic directly in the order class, making it hard to extend. The after code separates discount and coupon rules into classes, and a price engine applies them in sequence. This modular design allows easy addition of new discount types without changing core order logic.
### Before: naive discount logic mixed in order processing
class Order:
def __init__(self, items):
self.items = items
def calculate_total(self):
total = sum(item.price for item in self.items)
# Apply 10% discount if total > 100
if total > 100:
total *= 0.9
return total
### After: modular pricing strategy with discount and coupon rules
from abc import ABC, abstractmethod
class DiscountRule(ABC):
@abstractmethod
def apply(self, price, order):
pass
class TenPercentOver100(DiscountRule):
def apply(self, price, order):
total = sum(item.price for item in order.items)
if total > 100:
return price * 0.9
return price
class CouponRule(ABC):
@abstractmethod
def apply(self, price):
pass
class FixedCoupon(CouponRule):
def __init__(self, amount):
self.amount = amount
def apply(self, price):
return max(price - self.amount, 0)
class PriceEngine:
def __init__(self, discount_rules, coupon_rules):
self.discount_rules = discount_rules
self.coupon_rules = coupon_rules
def calculate_final_price(self, order):
price = sum(item.price for item in order.items)
for rule in self.discount_rules:
price = rule.apply(price, order)
for coupon in self.coupon_rules:
price = coupon.apply(price)
return price
# Usage example
class Item:
def __init__(self, price):
self.price = price
order = Order([Item(60), Item(50)])
discounts = [TenPercentOver100()]
coupons = [FixedCoupon(5)]
engine = PriceEngine(discounts, coupons)
final_price = engine.calculate_final_price(order)
print(final_price) # Outputs discounted and coupon-applied price