Design Patterns in the Age of AI
By: Pahlevi Fikri Auliya
By: Pahlevi Fikri Auliya
For a tree with branching factor B and depth D:
Number of leaves: O(BD)
Total number of nodes: O(BD+1−1B−1)=O(BD)
Examples:
Intended Audience
Non-Target Audience
Easy to Change, Test, and Extend
# Code Smell: Long Method
def process_order(order):
# Validate order
if not order.customer_id:
raise ValueError("Customer ID required")
if not order.items:
raise ValueError("Order must have items")
for item in order.items:
if item.quantity <= 0:
raise ValueError("Invalid quantity")
# Calculate totals
subtotal = sum(item.price * item.quantity for item in order.items)
tax = subtotal * 0.08
shipping = 10 if subtotal < 50 else 0
total = subtotal + tax + shipping
# Update inventory
for item in order.items:
inventory = get_inventory(item.product_id)
if inventory.quantity < item.quantity:
raise ValueError("Insufficient inventory")
inventory.quantity -= item.quantity
save_inventory(inventory)
# Process payment
payment_result = charge_credit_card(
order.customer.card_number,
total,
order.customer.card_cvv,
order.customer.card_expiry
)
# Send email
email_body = f"Order #{order.id} confirmed. Total: ${total}"
send_email(order.customer.email, "Order Confirmation", email_body)
# Log transaction
log_entry = f"Order {order.id} processed for ${total}"
write_to_log(log_entry)
return order
# Problem: Too many responsibilities in one method
# Hard to test, understand, and maintain
# Clean: Extracted Methods
def process_order(order):
validate_order(order)
total = calculate_order_total(order)
update_inventory(order)
process_payment(order, total)
send_confirmation_email(order, total)
log_transaction(order, total)
return order
def validate_order(order):
if not order.customer_id:
raise ValueError("Customer ID required")
if not order.items:
raise ValueError("Order must have items")
for item in order.items:
if item.quantity <= 0:
raise ValueError("Invalid quantity")
def calculate_order_total(order):
subtotal = sum(item.price * item.quantity for item in order.items)
tax = subtotal * 0.08
shipping = 10 if subtotal < 50 else 0
return subtotal + tax + shipping
def update_inventory(order):
for item in order.items:
inventory = get_inventory(item.product_id)
if inventory.quantity < item.quantity:
raise ValueError("Insufficient inventory")
inventory.quantity -= item.quantity
save_inventory(inventory)
def process_payment(order, total):
return charge_credit_card(
order.customer.card_number, total,
order.customer.card_cvv, order.customer.card_expiry
)
def send_confirmation_email(order, total):
email_body = f"Order #{order.id} confirmed. Total: ${total}"
send_email(order.customer.email, "Order Confirmation", email_body)
def log_transaction(order, total):
log_entry = f"Order {order.id} processed for ${total}"
write_to_log(log_entry)
# Benefits: Single responsibility, easy to test, reusable
# Code Smell: Deeply Nested Method
def process_user_data(users):
valid_users = []
for user in users:
if user is not None:
if user.active:
if user.email:
if '@' in user.email:
if user.age >= 18:
if user.country in ['US', 'CA', 'UK']:
if user.subscription_type:
if user.subscription_type == 'premium':
if user.payment_status == 'current':
if user.profile_complete:
user.priority = 'high'
valid_users.append(user)
else:
user.priority = 'medium'
valid_users.append(user)
else:
# Send payment reminder
send_payment_reminder(user)
else:
user.priority = 'low'
valid_users.append(user)
return valid_users
# Problem: Too many nested conditions, hard to follow logic
# Difficult to test individual conditions and maintain
# Clean: Early Returns and Guard Clauses
def process_user_data(users):
valid_users = []
for user in users:
processed_user = process_single_user(user)
if processed_user:
valid_users.append(processed_user)
return valid_users
def process_single_user(user):
# Guard clauses - early returns for invalid conditions
if not user or not user.active:
return None
if not is_valid_email(user.email):
return None
if user.age < 18:
return None
if user.country not in ['US', 'CA', 'UK']:
return None
# Process valid user
if not user.subscription_type:
return None
user.priority = determine_user_priority(user)
if user.subscription_type == 'premium' and user.payment_status != 'current':
send_payment_reminder(user)
return None
return user
def is_valid_email(email):
return email and '@' in email
def determine_user_priority(user):
if user.subscription_type == 'premium' and user.profile_complete:
return 'high'
elif user.subscription_type == 'premium':
return 'medium'
else:
return 'low'
# Benefits: Flattened structure, easier to read and test
# Each function has a single responsibility
# Code Smell: Long Class with too many responsibilities class UserAccount: def __init__(self, user_id): self.user_id = user_id self.profile = {} self.preferences = {} self.security_settings = {} self.payment_methods = [] self.order_history = [] self.notifications = [] # User profile management def update_profile(self, data): self.profile.update(data) def get_profile(self): return self.profile # Authentication & Security def change_password(self, old_password, new_password): if not self.verify_password(old_password): raise ValueError("Invalid password") self.security_settings['password_hash'] = hash_password(new_password) def enable_two_factor(self): self.security_settings['two_factor'] = True # Payment processing def add_payment_method(self, card_data): validated_card = self.validate_card(card_data) self.payment_methods.append(validated_card) def charge_payment(self, amount): primary_card = self.payment_methods[0] return process_charge(primary_card, amount) # Order management def create_order(self, items): order = Order(self.user_id, items) self.order_history.append(order) return order def get_order_history(self): return self.order_history # Notification handling def send_notification(self, message, type='email'): notification = {'message': message, 'type': type} self.notifications.append(notification) # Data export def export_user_data(self, format='json'): data = { 'profile': self.profile, 'orders': self.order_history, 'preferences': self.preferences } return serialize_data(data, format) # Problem: Single class handling too many different concerns # Violates Single Responsibility Principle
# Code Smell: Long Class with too many responsibilities class UserAccount: def __init__(self, user_id): self.user_id = user_id self.profile = {} self.preferences = {} self.security_settings = {} self.payment_methods = [] self.order_history = [] self.notifications = [] # User profile management def update_profile(self, data): self.profile.update(data) def get_profile(self): return self.profile # Authentication & Security def change_password(self, old_password, new_password): if not self.verify_password(old_password): raise ValueError("Invalid password") self.security_settings['password_hash'] = hash_password(new_password) def enable_two_factor(self): self.security_settings['two_factor'] = True # Payment processing def add_payment_method(self, card_data): validated_card = self.validate_card(card_data) self.payment_methods.append(validated_card) def charge_payment(self, amount): primary_card = self.payment_methods[0] return process_charge(primary_card, amount) # Order management def create_order(self, items): order = Order(self.user_id, items) self.order_history.append(order) return order def get_order_history(self): return self.order_history # Notification handling def send_notification(self, message, type='email'): notification = {'message': message, 'type': type} self.notifications.append(notification) # Data export def export_user_data(self, format='json'): data = { 'profile': self.profile, 'orders': self.order_history, 'preferences': self.preferences } return serialize_data(data, format) # Problem: Single class handling too many different concerns # Violates Single Responsibility Principle
# Code Smell: Long Class with too many responsibilities class UserAccount: def __init__(self, user_id): self.user_id = user_id self.profile = {} self.preferences = {} self.security_settings = {} self.payment_methods = [] self.order_history = [] self.notifications = [] # User profile management def update_profile(self, data): self.profile.update(data) def get_profile(self): return self.profile # Authentication & Security def change_password(self, old_password, new_password): if not self.verify_password(old_password): raise ValueError("Invalid password") self.security_settings['password_hash'] = hash_password(new_password) def enable_two_factor(self): self.security_settings['two_factor'] = True # Payment processing def add_payment_method(self, card_data): validated_card = self.validate_card(card_data) self.payment_methods.append(validated_card) def charge_payment(self, amount): primary_card = self.payment_methods[0] return process_charge(primary_card, amount) # Order management def create_order(self, items): order = Order(self.user_id, items) self.order_history.append(order) return order def get_order_history(self): return self.order_history # Notification handling def send_notification(self, message, type='email'): notification = {'message': message, 'type': type} self.notifications.append(notification) # Data export def export_user_data(self, format='json'): data = { 'profile': self.profile, 'orders': self.order_history, 'preferences': self.preferences } return serialize_data(data, format) # Problem: Single class handling too many different concerns # Violates Single Responsibility Principle
# Code Smell: Long Class with too many responsibilities class UserAccount: def __init__(self, user_id): self.user_id = user_id self.profile = {} self.preferences = {} self.security_settings = {} self.payment_methods = [] self.order_history = [] self.notifications = [] # User profile management def update_profile(self, data): self.profile.update(data) def get_profile(self): return self.profile # Authentication & Security def change_password(self, old_password, new_password): if not self.verify_password(old_password): raise ValueError("Invalid password") self.security_settings['password_hash'] = hash_password(new_password) def enable_two_factor(self): self.security_settings['two_factor'] = True # Payment processing def add_payment_method(self, card_data): validated_card = self.validate_card(card_data) self.payment_methods.append(validated_card) def charge_payment(self, amount): primary_card = self.payment_methods[0] return process_charge(primary_card, amount) # Order management def create_order(self, items): order = Order(self.user_id, items) self.order_history.append(order) return order def get_order_history(self): return self.order_history # Notification handling def send_notification(self, message, type='email'): notification = {'message': message, 'type': type} self.notifications.append(notification) # Data export def export_user_data(self, format='json'): data = { 'profile': self.profile, 'orders': self.order_history, 'preferences': self.preferences } return serialize_data(data, format) # Problem: Single class handling too many different concerns # Violates Single Responsibility Principle
# Code Smell: Long Class with too many responsibilities class UserAccount: def __init__(self, user_id): self.user_id = user_id self.profile = {} self.preferences = {} self.security_settings = {} self.payment_methods = [] self.order_history = [] self.notifications = [] # User profile management def update_profile(self, data): self.profile.update(data) def get_profile(self): return self.profile # Authentication & Security def change_password(self, old_password, new_password): if not self.verify_password(old_password): raise ValueError("Invalid password") self.security_settings['password_hash'] = hash_password(new_password) def enable_two_factor(self): self.security_settings['two_factor'] = True # Payment processing def add_payment_method(self, card_data): validated_card = self.validate_card(card_data) self.payment_methods.append(validated_card) def charge_payment(self, amount): primary_card = self.payment_methods[0] return process_charge(primary_card, amount) # Order management def create_order(self, items): order = Order(self.user_id, items) self.order_history.append(order) return order def get_order_history(self): return self.order_history # Notification handling def send_notification(self, message, type='email'): notification = {'message': message, 'type': type} self.notifications.append(notification) # Data export def export_user_data(self, format='json'): data = { 'profile': self.profile, 'orders': self.order_history, 'preferences': self.preferences } return serialize_data(data, format) # Problem: Single class handling too many different concerns # Violates Single Responsibility Principle
# Code Smell: Long Class with too many responsibilities class UserAccount: def __init__(self, user_id): self.user_id = user_id self.profile = {} self.preferences = {} self.security_settings = {} self.payment_methods = [] self.order_history = [] self.notifications = [] # User profile management def update_profile(self, data): self.profile.update(data) def get_profile(self): return self.profile # Authentication & Security def change_password(self, old_password, new_password): if not self.verify_password(old_password): raise ValueError("Invalid password") self.security_settings['password_hash'] = hash_password(new_password) def enable_two_factor(self): self.security_settings['two_factor'] = True # Payment processing def add_payment_method(self, card_data): validated_card = self.validate_card(card_data) self.payment_methods.append(validated_card) def charge_payment(self, amount): primary_card = self.payment_methods[0] return process_charge(primary_card, amount) # Order management def create_order(self, items): order = Order(self.user_id, items) self.order_history.append(order) return order def get_order_history(self): return self.order_history # Notification handling def send_notification(self, message, type='email'): notification = {'message': message, 'type': type} self.notifications.append(notification) # Data export def export_user_data(self, format='json'): data = { 'profile': self.profile, 'orders': self.order_history, 'preferences': self.preferences } return serialize_data(data, format) # Problem: Single class handling too many different concerns # Violates Single Responsibility Principle
# Code Smell: Long Class with too many responsibilities class UserAccount: def __init__(self, user_id): self.user_id = user_id self.profile = {} self.preferences = {} self.security_settings = {} self.payment_methods = [] self.order_history = [] self.notifications = [] # User profile management def update_profile(self, data): self.profile.update(data) def get_profile(self): return self.profile # Authentication & Security def change_password(self, old_password, new_password): if not self.verify_password(old_password): raise ValueError("Invalid password") self.security_settings['password_hash'] = hash_password(new_password) def enable_two_factor(self): self.security_settings['two_factor'] = True # Payment processing def add_payment_method(self, card_data): validated_card = self.validate_card(card_data) self.payment_methods.append(validated_card) def charge_payment(self, amount): primary_card = self.payment_methods[0] return process_charge(primary_card, amount) # Order management def create_order(self, items): order = Order(self.user_id, items) self.order_history.append(order) return order def get_order_history(self): return self.order_history # Notification handling def send_notification(self, message, type='email'): notification = {'message': message, 'type': type} self.notifications.append(notification) # Data export def export_user_data(self, format='json'): data = { 'profile': self.profile, 'orders': self.order_history, 'preferences': self.preferences } return serialize_data(data, format) # Problem: Single class handling too many different concerns # Violates Single Responsibility Principle
# Clean: Separated into focused classes class User: def __init__(self, user_id): self.user_id = user_id self.profile = UserProfile(user_id) self.security = SecurityManager(user_id) self.payment = PaymentManager(user_id) self.orders = OrderManager(user_id) class UserProfile: def __init__(self, user_id): self.user_id = user_id self.data = {} self.preferences = {} def update_profile(self, data): self.data.update(data) def get_profile(self): return self.data class SecurityManager: def __init__(self, user_id): self.user_id = user_id self.settings = {} def change_password(self, old_password, new_password): if not self.verify_password(old_password): raise ValueError("Invalid password") self.settings['password_hash'] = hash_password(new_password) def enable_two_factor(self): self.settings['two_factor'] = True class PaymentManager: def __init__(self, user_id): self.user_id = user_id self.payment_methods = [] def add_payment_method(self, card_data): validated_card = self.validate_card(card_data) self.payment_methods.append(validated_card) def charge_payment(self, amount): primary_card = self.payment_methods[0] return process_charge(primary_card, amount) class OrderManager: def __init__(self, user_id): self.user_id = user_id self.order_history = [] def create_order(self, items): order = Order(self.user_id, items) self.order_history.append(order) return order # Benefits: Each class has single responsibility # Easier to test, maintain, and extend individual components
# Clean: Separated into focused classes class User: def __init__(self, user_id): self.user_id = user_id self.profile = UserProfile(user_id) self.security = SecurityManager(user_id) self.payment = PaymentManager(user_id) self.orders = OrderManager(user_id) class UserProfile: def __init__(self, user_id): self.user_id = user_id self.data = {} self.preferences = {} def update_profile(self, data): self.data.update(data) def get_profile(self): return self.data class SecurityManager: def __init__(self, user_id): self.user_id = user_id self.settings = {} def change_password(self, old_password, new_password): if not self.verify_password(old_password): raise ValueError("Invalid password") self.settings['password_hash'] = hash_password(new_password) def enable_two_factor(self): self.settings['two_factor'] = True class PaymentManager: def __init__(self, user_id): self.user_id = user_id self.payment_methods = [] def add_payment_method(self, card_data): validated_card = self.validate_card(card_data) self.payment_methods.append(validated_card) def charge_payment(self, amount): primary_card = self.payment_methods[0] return process_charge(primary_card, amount) class OrderManager: def __init__(self, user_id): self.user_id = user_id self.order_history = [] def create_order(self, items): order = Order(self.user_id, items) self.order_history.append(order) return order # Benefits: Each class has single responsibility # Easier to test, maintain, and extend individual components
# Clean: Separated into focused classes class User: def __init__(self, user_id): self.user_id = user_id self.profile = UserProfile(user_id) self.security = SecurityManager(user_id) self.payment = PaymentManager(user_id) self.orders = OrderManager(user_id) class UserProfile: def __init__(self, user_id): self.user_id = user_id self.data = {} self.preferences = {} def update_profile(self, data): self.data.update(data) def get_profile(self): return self.data class SecurityManager: def __init__(self, user_id): self.user_id = user_id self.settings = {} def change_password(self, old_password, new_password): if not self.verify_password(old_password): raise ValueError("Invalid password") self.settings['password_hash'] = hash_password(new_password) def enable_two_factor(self): self.settings['two_factor'] = True class PaymentManager: def __init__(self, user_id): self.user_id = user_id self.payment_methods = [] def add_payment_method(self, card_data): validated_card = self.validate_card(card_data) self.payment_methods.append(validated_card) def charge_payment(self, amount): primary_card = self.payment_methods[0] return process_charge(primary_card, amount) class OrderManager: def __init__(self, user_id): self.user_id = user_id self.order_history = [] def create_order(self, items): order = Order(self.user_id, items) self.order_history.append(order) return order # Benefits: Each class has single responsibility # Easier to test, maintain, and extend individual components
# Clean: Separated into focused classes class User: def __init__(self, user_id): self.user_id = user_id self.profile = UserProfile(user_id) self.security = SecurityManager(user_id) self.payment = PaymentManager(user_id) self.orders = OrderManager(user_id) class UserProfile: def __init__(self, user_id): self.user_id = user_id self.data = {} self.preferences = {} def update_profile(self, data): self.data.update(data) def get_profile(self): return self.data class SecurityManager: def __init__(self, user_id): self.user_id = user_id self.settings = {} def change_password(self, old_password, new_password): if not self.verify_password(old_password): raise ValueError("Invalid password") self.settings['password_hash'] = hash_password(new_password) def enable_two_factor(self): self.settings['two_factor'] = True class PaymentManager: def __init__(self, user_id): self.user_id = user_id self.payment_methods = [] def add_payment_method(self, card_data): validated_card = self.validate_card(card_data) self.payment_methods.append(validated_card) def charge_payment(self, amount): primary_card = self.payment_methods[0] return process_charge(primary_card, amount) class OrderManager: def __init__(self, user_id): self.user_id = user_id self.order_history = [] def create_order(self, items): order = Order(self.user_id, items) self.order_history.append(order) return order # Benefits: Each class has single responsibility # Easier to test, maintain, and extend individual components
# Clean: Separated into focused classes class User: def __init__(self, user_id): self.user_id = user_id self.profile = UserProfile(user_id) self.security = SecurityManager(user_id) self.payment = PaymentManager(user_id) self.orders = OrderManager(user_id) class UserProfile: def __init__(self, user_id): self.user_id = user_id self.data = {} self.preferences = {} def update_profile(self, data): self.data.update(data) def get_profile(self): return self.data class SecurityManager: def __init__(self, user_id): self.user_id = user_id self.settings = {} def change_password(self, old_password, new_password): if not self.verify_password(old_password): raise ValueError("Invalid password") self.settings['password_hash'] = hash_password(new_password) def enable_two_factor(self): self.settings['two_factor'] = True class PaymentManager: def __init__(self, user_id): self.user_id = user_id self.payment_methods = [] def add_payment_method(self, card_data): validated_card = self.validate_card(card_data) self.payment_methods.append(validated_card) def charge_payment(self, amount): primary_card = self.payment_methods[0] return process_charge(primary_card, amount) class OrderManager: def __init__(self, user_id): self.user_id = user_id self.order_history = [] def create_order(self, items): order = Order(self.user_id, items) self.order_history.append(order) return order # Benefits: Each class has single responsibility # Easier to test, maintain, and extend individual components
# Clean: Separated into focused classes class User: def __init__(self, user_id): self.user_id = user_id self.profile = UserProfile(user_id) self.security = SecurityManager(user_id) self.payment = PaymentManager(user_id) self.orders = OrderManager(user_id) class UserProfile: def __init__(self, user_id): self.user_id = user_id self.data = {} self.preferences = {} def update_profile(self, data): self.data.update(data) def get_profile(self): return self.data class SecurityManager: def __init__(self, user_id): self.user_id = user_id self.settings = {} def change_password(self, old_password, new_password): if not self.verify_password(old_password): raise ValueError("Invalid password") self.settings['password_hash'] = hash_password(new_password) def enable_two_factor(self): self.settings['two_factor'] = True class PaymentManager: def __init__(self, user_id): self.user_id = user_id self.payment_methods = [] def add_payment_method(self, card_data): validated_card = self.validate_card(card_data) self.payment_methods.append(validated_card) def charge_payment(self, amount): primary_card = self.payment_methods[0] return process_charge(primary_card, amount) class OrderManager: def __init__(self, user_id): self.user_id = user_id self.order_history = [] def create_order(self, items): order = Order(self.user_id, items) self.order_history.append(order) return order # Benefits: Each class has single responsibility # Easier to test, maintain, and extend individual components
# Code Smell: Complex Switch Statement def calculate_shipping_cost(shipping_type, weight, distance): if shipping_type == "standard": base_rate = 5.0 weight_rate = 0.5 * weight distance_rate = 0.01 * distance return base_rate + weight_rate + distance_rate elif shipping_type == "express": base_rate = 15.0 weight_rate = 1.0 * weight distance_rate = 0.02 * distance return base_rate + weight_rate + distance_rate elif shipping_type == "overnight": base_rate = 25.0 weight_rate = 2.0 * weight distance_rate = 0.05 * distance return base_rate + weight_rate + distance_rate elif shipping_type == "international": base_rate = 50.0 weight_rate = 3.0 * weight customs_fee = 10.0 return base_rate + weight_rate + customs_fee else: raise ValueError(f"Unknown shipping type: {shipping_type}") # Problem: Adding new shipping types requires modifying this function # Violates Open/Closed Principle
# Code Smell: Complex Switch Statement def calculate_shipping_cost(shipping_type, weight, distance): if shipping_type == "standard": base_rate = 5.0 weight_rate = 0.5 * weight distance_rate = 0.01 * distance return base_rate + weight_rate + distance_rate elif shipping_type == "express": base_rate = 15.0 weight_rate = 1.0 * weight distance_rate = 0.02 * distance return base_rate + weight_rate + distance_rate elif shipping_type == "overnight": base_rate = 25.0 weight_rate = 2.0 * weight distance_rate = 0.05 * distance return base_rate + weight_rate + distance_rate elif shipping_type == "international": base_rate = 50.0 weight_rate = 3.0 * weight customs_fee = 10.0 return base_rate + weight_rate + customs_fee else: raise ValueError(f"Unknown shipping type: {shipping_type}") # Problem: Adding new shipping types requires modifying this function # Violates Open/Closed Principle
# Clean: Strategy Pattern (Polymorphism) from abc import ABC, abstractmethod class ShippingCalculator(ABC): @abstractmethod def calculate_cost(self, weight, distance): pass class StandardShipping(ShippingCalculator): def calculate_cost(self, weight, distance): base_rate = 5.0 weight_rate = 0.5 * weight distance_rate = 0.01 * distance return base_rate + weight_rate + distance_rate class ExpressShipping(ShippingCalculator): def calculate_cost(self, weight, distance): base_rate = 15.0 weight_rate = 1.0 * weight distance_rate = 0.02 * distance return base_rate + weight_rate + distance_rate class OvernightShipping(ShippingCalculator): def calculate_cost(self, weight, distance): base_rate = 25.0 weight_rate = 2.0 * weight distance_rate = 0.05 * distance return base_rate + weight_rate + distance_rate class ShippingService: def __init__(self): self.calculators = { "standard": StandardShipping(), "express": ExpressShipping(), "overnight": OvernightShipping() } def calculate_cost(self, shipping_type, weight, distance): calculator = self.calculators.get(shipping_type) if not calculator: raise ValueError(f"Unknown shipping type: {shipping_type}") return calculator.calculate_cost(weight, distance) # Benefits: Easy to extend, follows Open/Closed Principle # Each strategy is testable independently
# Clean: Strategy Pattern (Polymorphism) from abc import ABC, abstractmethod class ShippingCalculator(ABC): @abstractmethod def calculate_cost(self, weight, distance): pass class StandardShipping(ShippingCalculator): def calculate_cost(self, weight, distance): base_rate = 5.0 weight_rate = 0.5 * weight distance_rate = 0.01 * distance return base_rate + weight_rate + distance_rate class ExpressShipping(ShippingCalculator): def calculate_cost(self, weight, distance): base_rate = 15.0 weight_rate = 1.0 * weight distance_rate = 0.02 * distance return base_rate + weight_rate + distance_rate class OvernightShipping(ShippingCalculator): def calculate_cost(self, weight, distance): base_rate = 25.0 weight_rate = 2.0 * weight distance_rate = 0.05 * distance return base_rate + weight_rate + distance_rate class ShippingService: def __init__(self): self.calculators = { "standard": StandardShipping(), "express": ExpressShipping(), "overnight": OvernightShipping() } def calculate_cost(self, shipping_type, weight, distance): calculator = self.calculators.get(shipping_type) if not calculator: raise ValueError(f"Unknown shipping type: {shipping_type}") return calculator.calculate_cost(weight, distance) # Benefits: Easy to extend, follows Open/Closed Principle # Each strategy is testable independently
# Clean: Strategy Pattern (Polymorphism) from abc import ABC, abstractmethod class ShippingCalculator(ABC): @abstractmethod def calculate_cost(self, weight, distance): pass class StandardShipping(ShippingCalculator): def calculate_cost(self, weight, distance): base_rate = 5.0 weight_rate = 0.5 * weight distance_rate = 0.01 * distance return base_rate + weight_rate + distance_rate class ExpressShipping(ShippingCalculator): def calculate_cost(self, weight, distance): base_rate = 15.0 weight_rate = 1.0 * weight distance_rate = 0.02 * distance return base_rate + weight_rate + distance_rate class OvernightShipping(ShippingCalculator): def calculate_cost(self, weight, distance): base_rate = 25.0 weight_rate = 2.0 * weight distance_rate = 0.05 * distance return base_rate + weight_rate + distance_rate class ShippingService: def __init__(self): self.calculators = { "standard": StandardShipping(), "express": ExpressShipping(), "overnight": OvernightShipping() } def calculate_cost(self, shipping_type, weight, distance): calculator = self.calculators.get(shipping_type) if not calculator: raise ValueError(f"Unknown shipping type: {shipping_type}") return calculator.calculate_cost(weight, distance) # Benefits: Easy to extend, follows Open/Closed Principle # Each strategy is testable independently
# Clean: Strategy Pattern (Polymorphism) from abc import ABC, abstractmethod class ShippingCalculator(ABC): @abstractmethod def calculate_cost(self, weight, distance): pass class StandardShipping(ShippingCalculator): def calculate_cost(self, weight, distance): base_rate = 5.0 weight_rate = 0.5 * weight distance_rate = 0.01 * distance return base_rate + weight_rate + distance_rate class ExpressShipping(ShippingCalculator): def calculate_cost(self, weight, distance): base_rate = 15.0 weight_rate = 1.0 * weight distance_rate = 0.02 * distance return base_rate + weight_rate + distance_rate class OvernightShipping(ShippingCalculator): def calculate_cost(self, weight, distance): base_rate = 25.0 weight_rate = 2.0 * weight distance_rate = 0.05 * distance return base_rate + weight_rate + distance_rate class ShippingService: def __init__(self): self.calculators = { "standard": StandardShipping(), "express": ExpressShipping(), "overnight": OvernightShipping() } def calculate_cost(self, shipping_type, weight, distance): calculator = self.calculators.get(shipping_type) if not calculator: raise ValueError(f"Unknown shipping type: {shipping_type}") return calculator.calculate_cost(weight, distance) # Benefits: Easy to extend, follows Open/Closed Principle # Each strategy is testable independently
# Clean: Strategy Pattern (Polymorphism) from abc import ABC, abstractmethod class ShippingCalculator(ABC): @abstractmethod def calculate_cost(self, weight, distance): pass class StandardShipping(ShippingCalculator): def calculate_cost(self, weight, distance): base_rate = 5.0 weight_rate = 0.5 * weight distance_rate = 0.01 * distance return base_rate + weight_rate + distance_rate class ExpressShipping(ShippingCalculator): def calculate_cost(self, weight, distance): base_rate = 15.0 weight_rate = 1.0 * weight distance_rate = 0.02 * distance return base_rate + weight_rate + distance_rate class OvernightShipping(ShippingCalculator): def calculate_cost(self, weight, distance): base_rate = 25.0 weight_rate = 2.0 * weight distance_rate = 0.05 * distance return base_rate + weight_rate + distance_rate class ShippingService: def __init__(self): self.calculators = { "standard": StandardShipping(), "express": ExpressShipping(), "overnight": OvernightShipping() } def calculate_cost(self, shipping_type, weight, distance): calculator = self.calculators.get(shipping_type) if not calculator: raise ValueError(f"Unknown shipping type: {shipping_type}") return calculator.calculate_cost(weight, distance) # Benefits: Easy to extend, follows Open/Closed Principle # Each strategy is testable independently
# Clean: Strategy Pattern (Polymorphism) from abc import ABC, abstractmethod class ShippingCalculator(ABC): @abstractmethod def calculate_cost(self, weight, distance): pass class StandardShipping(ShippingCalculator): def calculate_cost(self, weight, distance): base_rate = 5.0 weight_rate = 0.5 * weight distance_rate = 0.01 * distance return base_rate + weight_rate + distance_rate class ExpressShipping(ShippingCalculator): def calculate_cost(self, weight, distance): base_rate = 15.0 weight_rate = 1.0 * weight distance_rate = 0.02 * distance return base_rate + weight_rate + distance_rate class OvernightShipping(ShippingCalculator): def calculate_cost(self, weight, distance): base_rate = 25.0 weight_rate = 2.0 * weight distance_rate = 0.05 * distance return base_rate + weight_rate + distance_rate class ShippingService: def __init__(self): self.calculators = { "standard": StandardShipping(), "express": ExpressShipping(), "overnight": OvernightShipping() } def calculate_cost(self, shipping_type, weight, distance): calculator = self.calculators.get(shipping_type) if not calculator: raise ValueError(f"Unknown shipping type: {shipping_type}") return calculator.calculate_cost(weight, distance) # Benefits: Easy to extend, follows Open/Closed Principle # Each strategy is testable independently
# Clean: Strategy Pattern (Polymorphism) from abc import ABC, abstractmethod class ShippingCalculator(ABC): @abstractmethod def calculate_cost(self, weight, distance): pass class StandardShipping(ShippingCalculator): def calculate_cost(self, weight, distance): base_rate = 5.0 weight_rate = 0.5 * weight distance_rate = 0.01 * distance return base_rate + weight_rate + distance_rate class ExpressShipping(ShippingCalculator): def calculate_cost(self, weight, distance): base_rate = 15.0 weight_rate = 1.0 * weight distance_rate = 0.02 * distance return base_rate + weight_rate + distance_rate class OvernightShipping(ShippingCalculator): def calculate_cost(self, weight, distance): base_rate = 25.0 weight_rate = 2.0 * weight distance_rate = 0.05 * distance return base_rate + weight_rate + distance_rate class ShippingService: def __init__(self): self.calculators = { "standard": StandardShipping(), "express": ExpressShipping(), "overnight": OvernightShipping() } def calculate_cost(self, shipping_type, weight, distance): calculator = self.calculators.get(shipping_type) if not calculator: raise ValueError(f"Unknown shipping type: {shipping_type}") return calculator.calculate_cost(weight, distance) # Benefits: Easy to extend, follows Open/Closed Principle # Each strategy is testable independently
DRY: Don’t Repeat Yourself
# Code Smell: Duplicate Code def send_welcome_email(user): subject = "Welcome to Our Platform!" body = f""" Dear {user.name}, Welcome to our platform! We're excited to have you. Best regards, The Team """ smtp_server = smtplib.SMTP('smtp.gmail.com', 587) smtp_server.starttls() smtp_server.login('noreply@company.com', 'password') smtp_server.send_message(user.email, subject, body) smtp_server.quit() def send_password_reset_email(user, reset_link): subject = "Password Reset Request" body = f""" Dear {user.name}, Click here to reset your password: {reset_link} Best regards, The Team """ smtp_server = smtplib.SMTP('smtp.gmail.com', 587) smtp_server.starttls() smtp_server.login('noreply@company.com', 'password') smtp_server.send_message(user.email, subject, body) smtp_server.quit() # Problem: SMTP setup code is duplicated # Changes to email config require updating multiple places
# Code Smell: Duplicate Code def send_welcome_email(user): subject = "Welcome to Our Platform!" body = f""" Dear {user.name}, Welcome to our platform! We're excited to have you. Best regards, The Team """ smtp_server = smtplib.SMTP('smtp.gmail.com', 587) smtp_server.starttls() smtp_server.login('noreply@company.com', 'password') smtp_server.send_message(user.email, subject, body) smtp_server.quit() def send_password_reset_email(user, reset_link): subject = "Password Reset Request" body = f""" Dear {user.name}, Click here to reset your password: {reset_link} Best regards, The Team """ smtp_server = smtplib.SMTP('smtp.gmail.com', 587) smtp_server.starttls() smtp_server.login('noreply@company.com', 'password') smtp_server.send_message(user.email, subject, body) smtp_server.quit() # Problem: SMTP setup code is duplicated # Changes to email config require updating multiple places
# Code Smell: Duplicate Code def send_welcome_email(user): subject = "Welcome to Our Platform!" body = f""" Dear {user.name}, Welcome to our platform! We're excited to have you. Best regards, The Team """ smtp_server = smtplib.SMTP('smtp.gmail.com', 587) smtp_server.starttls() smtp_server.login('noreply@company.com', 'password') smtp_server.send_message(user.email, subject, body) smtp_server.quit() def send_password_reset_email(user, reset_link): subject = "Password Reset Request" body = f""" Dear {user.name}, Click here to reset your password: {reset_link} Best regards, The Team """ smtp_server = smtplib.SMTP('smtp.gmail.com', 587) smtp_server.starttls() smtp_server.login('noreply@company.com', 'password') smtp_server.send_message(user.email, subject, body) smtp_server.quit() # Problem: SMTP setup code is duplicated # Changes to email config require updating multiple places
# Clean: Extract Common Functionality class EmailService: def __init__(self): self.smtp_host = 'smtp.gmail.com' self.smtp_port = 587 self.email = 'noreply@company.com' self.password = 'password' def send_email(self, to_email, subject, body): smtp_server = smtplib.SMTP(self.smtp_host, self.smtp_port) smtp_server.starttls() smtp_server.login(self.email, self.password) smtp_server.send_message(to_email, subject, body) smtp_server.quit() class UserNotificationService: def __init__(self, email_service): self.email_service = email_service def send_welcome_email(self, user): subject = "Welcome to Our Platform!" body = self._create_email_body( f"Welcome to our platform! We're excited to have you.", user.name ) self.email_service.send_email(user.email, subject, body) def send_password_reset_email(self, user, reset_link): subject = "Password Reset Request" body = self._create_email_body( f"Click here to reset your password: {reset_link}", user.name ) self.email_service.send_email(user.email, subject, body) def _create_email_body(self, message, user_name): return f""" Dear {user_name}, {message} Best regards, The Team """ # Benefits: Single source of truth, easy to modify email config # Email template logic is also reusable
# Clean: Extract Common Functionality class EmailService: def __init__(self): self.smtp_host = 'smtp.gmail.com' self.smtp_port = 587 self.email = 'noreply@company.com' self.password = 'password' def send_email(self, to_email, subject, body): smtp_server = smtplib.SMTP(self.smtp_host, self.smtp_port) smtp_server.starttls() smtp_server.login(self.email, self.password) smtp_server.send_message(to_email, subject, body) smtp_server.quit() class UserNotificationService: def __init__(self, email_service): self.email_service = email_service def send_welcome_email(self, user): subject = "Welcome to Our Platform!" body = self._create_email_body( f"Welcome to our platform! We're excited to have you.", user.name ) self.email_service.send_email(user.email, subject, body) def send_password_reset_email(self, user, reset_link): subject = "Password Reset Request" body = self._create_email_body( f"Click here to reset your password: {reset_link}", user.name ) self.email_service.send_email(user.email, subject, body) def _create_email_body(self, message, user_name): return f""" Dear {user_name}, {message} Best regards, The Team """ # Benefits: Single source of truth, easy to modify email config # Email template logic is also reusable
# Clean: Extract Common Functionality class EmailService: def __init__(self): self.smtp_host = 'smtp.gmail.com' self.smtp_port = 587 self.email = 'noreply@company.com' self.password = 'password' def send_email(self, to_email, subject, body): smtp_server = smtplib.SMTP(self.smtp_host, self.smtp_port) smtp_server.starttls() smtp_server.login(self.email, self.password) smtp_server.send_message(to_email, subject, body) smtp_server.quit() class UserNotificationService: def __init__(self, email_service): self.email_service = email_service def send_welcome_email(self, user): subject = "Welcome to Our Platform!" body = self._create_email_body( f"Welcome to our platform! We're excited to have you.", user.name ) self.email_service.send_email(user.email, subject, body) def send_password_reset_email(self, user, reset_link): subject = "Password Reset Request" body = self._create_email_body( f"Click here to reset your password: {reset_link}", user.name ) self.email_service.send_email(user.email, subject, body) def _create_email_body(self, message, user_name): return f""" Dear {user_name}, {message} Best regards, The Team """ # Benefits: Single source of truth, easy to modify email config # Email template logic is also reusable
# Code Smell: Premature abstraction class PaymentProcessor: def __init__(self): # Preparing for multiple payment methods that don't exist yet self.payment_strategies = {} self.payment_validators = {} self.payment_loggers = {} self.payment_retry_policies = {} self.payment_notification_handlers = {} def register_payment_method(self, name, strategy, validator, logger, retry_policy, notifier): self.payment_strategies[name] = strategy self.payment_validators[name] = validator self.payment_loggers[name] = logger self.payment_retry_policies[name] = retry_policy self.payment_notification_handlers[name] = notifier def process_payment(self, payment_method, amount): # Complex logic for handling different payment methods if payment_method not in self.payment_strategies: raise ValueError("Unknown payment method") validator = self.payment_validators[payment_method] if not validator.validate(amount): return False strategy = self.payment_strategies[payment_method] retry_policy = self.payment_retry_policies[payment_method] # Retry logic that we don't actually need attempts = 0 while attempts < retry_policy.max_attempts: try: result = strategy.process(amount) self.payment_loggers[payment_method].log(result) self.payment_notification_handlers[payment_method].notify(result) return result except Exception as e: attempts += 1 if attempts >= retry_policy.max_attempts: raise # Problem: Over-engineered for requirements that may never come # Current requirement: Only process credit card payments
# Code Smell: Premature abstraction class PaymentProcessor: def __init__(self): # Preparing for multiple payment methods that don't exist yet self.payment_strategies = {} self.payment_validators = {} self.payment_loggers = {} self.payment_retry_policies = {} self.payment_notification_handlers = {} def register_payment_method(self, name, strategy, validator, logger, retry_policy, notifier): self.payment_strategies[name] = strategy self.payment_validators[name] = validator self.payment_loggers[name] = logger self.payment_retry_policies[name] = retry_policy self.payment_notification_handlers[name] = notifier def process_payment(self, payment_method, amount): # Complex logic for handling different payment methods if payment_method not in self.payment_strategies: raise ValueError("Unknown payment method") validator = self.payment_validators[payment_method] if not validator.validate(amount): return False strategy = self.payment_strategies[payment_method] retry_policy = self.payment_retry_policies[payment_method] # Retry logic that we don't actually need attempts = 0 while attempts < retry_policy.max_attempts: try: result = strategy.process(amount) self.payment_loggers[payment_method].log(result) self.payment_notification_handlers[payment_method].notify(result) return result except Exception as e: attempts += 1 if attempts >= retry_policy.max_attempts: raise # Problem: Over-engineered for requirements that may never come # Current requirement: Only process credit card payments
# Code Smell: Premature abstraction class PaymentProcessor: def __init__(self): # Preparing for multiple payment methods that don't exist yet self.payment_strategies = {} self.payment_validators = {} self.payment_loggers = {} self.payment_retry_policies = {} self.payment_notification_handlers = {} def register_payment_method(self, name, strategy, validator, logger, retry_policy, notifier): self.payment_strategies[name] = strategy self.payment_validators[name] = validator self.payment_loggers[name] = logger self.payment_retry_policies[name] = retry_policy self.payment_notification_handlers[name] = notifier def process_payment(self, payment_method, amount): # Complex logic for handling different payment methods if payment_method not in self.payment_strategies: raise ValueError("Unknown payment method") validator = self.payment_validators[payment_method] if not validator.validate(amount): return False strategy = self.payment_strategies[payment_method] retry_policy = self.payment_retry_policies[payment_method] # Retry logic that we don't actually need attempts = 0 while attempts < retry_policy.max_attempts: try: result = strategy.process(amount) self.payment_loggers[payment_method].log(result) self.payment_notification_handlers[payment_method].notify(result) return result except Exception as e: attempts += 1 if attempts >= retry_policy.max_attempts: raise # Problem: Over-engineered for requirements that may never come # Current requirement: Only process credit card payments
# Clean: Simple solution for current needs class PaymentProcessor: def process_credit_card_payment(self, card_number, amount): # Validate amount if amount <= 0: raise ValueError("Amount must be positive") # Process payment result = self.charge_card(card_number, amount) # Log transaction self.log_transaction(f"Charged ${amount} to card {card_number[-4:]}") return result def charge_card(self, card_number, amount): # Actual payment processing logic return {"status": "success", "transaction_id": "TXN123"} def log_transaction(self, message): print(f"[{datetime.now()}] {message}") # Benefits: # - Simple and direct # - Easy to understand and test # - Can be refactored when new requirements arrive # - No wasted effort on unused abstractions
# Clean: Simple solution for current needs class PaymentProcessor: def process_credit_card_payment(self, card_number, amount): # Validate amount if amount <= 0: raise ValueError("Amount must be positive") # Process payment result = self.charge_card(card_number, amount) # Log transaction self.log_transaction(f"Charged ${amount} to card {card_number[-4:]}") return result def charge_card(self, card_number, amount): # Actual payment processing logic return {"status": "success", "transaction_id": "TXN123"} def log_transaction(self, message): print(f"[{datetime.now()}] {message}") # Benefits: # - Simple and direct # - Easy to understand and test # - Can be refactored when new requirements arrive # - No wasted effort on unused abstractions
# Code Smell: Asking for data instead of telling to do class ShoppingCart: def __init__(self): self.items = [] def get_items(self): return self.items class CheckoutService: def calculate_total(self, cart): # Asking cart for data and doing the work ourselves items = cart.get_items() total = 0 for item in items: total += item.price * item.quantity return total def apply_discount(self, cart, discount_percentage): # Again asking for data items = cart.get_items() total = 0 for item in items: total += item.price * item.quantity discount = total * (discount_percentage / 100) return total - discount # Problem: CheckoutService knows too much about cart internals # Violates encapsulation - cart should manage its own data
# Code Smell: Asking for data instead of telling to do class ShoppingCart: def __init__(self): self.items = [] def get_items(self): return self.items class CheckoutService: def calculate_total(self, cart): # Asking cart for data and doing the work ourselves items = cart.get_items() total = 0 for item in items: total += item.price * item.quantity return total def apply_discount(self, cart, discount_percentage): # Again asking for data items = cart.get_items() total = 0 for item in items: total += item.price * item.quantity discount = total * (discount_percentage / 100) return total - discount # Problem: CheckoutService knows too much about cart internals # Violates encapsulation - cart should manage its own data
# Code Smell: Asking for data instead of telling to do class ShoppingCart: def __init__(self): self.items = [] def get_items(self): return self.items class CheckoutService: def calculate_total(self, cart): # Asking cart for data and doing the work ourselves items = cart.get_items() total = 0 for item in items: total += item.price * item.quantity return total def apply_discount(self, cart, discount_percentage): # Again asking for data items = cart.get_items() total = 0 for item in items: total += item.price * item.quantity discount = total * (discount_percentage / 100) return total - discount # Problem: CheckoutService knows too much about cart internals # Violates encapsulation - cart should manage its own data
# Clean: Tell objects what to do class ShoppingCart: def __init__(self): self.items = [] def add_item(self, item): self.items.append(item) def calculate_total(self): # Cart knows how to calculate its own total return sum(item.price * item.quantity for item in self.items) def apply_discount(self, discount_percentage): # Cart manages its own discount logic total = self.calculate_total() discount = total * (discount_percentage / 100) return total - discount class CheckoutService: def process_checkout(self, cart, discount_percentage=0): # Tell cart what to do, don't ask for its data if discount_percentage > 0: return cart.apply_discount(discount_percentage) else: return cart.calculate_total() # Benefits: # - Better encapsulation # - Cart is responsible for its own data # - Easier to change cart implementation # - Less coupling between classes
# Clean: Tell objects what to do class ShoppingCart: def __init__(self): self.items = [] def add_item(self, item): self.items.append(item) def calculate_total(self): # Cart knows how to calculate its own total return sum(item.price * item.quantity for item in self.items) def apply_discount(self, discount_percentage): # Cart manages its own discount logic total = self.calculate_total() discount = total * (discount_percentage / 100) return total - discount class CheckoutService: def process_checkout(self, cart, discount_percentage=0): # Tell cart what to do, don't ask for its data if discount_percentage > 0: return cart.apply_discount(discount_percentage) else: return cart.calculate_total() # Benefits: # - Better encapsulation # - Cart is responsible for its own data # - Easier to change cart implementation # - Less coupling between classes
# Clean: Tell objects what to do class ShoppingCart: def __init__(self): self.items = [] def add_item(self, item): self.items.append(item) def calculate_total(self): # Cart knows how to calculate its own total return sum(item.price * item.quantity for item in self.items) def apply_discount(self, discount_percentage): # Cart manages its own discount logic total = self.calculate_total() discount = total * (discount_percentage / 100) return total - discount class CheckoutService: def process_checkout(self, cart, discount_percentage=0): # Tell cart what to do, don't ask for its data if discount_percentage > 0: return cart.apply_discount(discount_percentage) else: return cart.calculate_total() # Benefits: # - Better encapsulation # - Cart is responsible for its own data # - Easier to change cart implementation # - Less coupling between classes
# Code Smell: Overly complex solution def validate_email(email): # Complex regex that's hard to understand and maintain import re # RFC 5322 compliant email regex (simplified version) pattern = r''' ^(?:[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*| "(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]| \\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@ (?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)*[a-zA-Z0-9] (?:[a-zA-Z0-9-]*[a-zA-Z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])| 1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])| 1[0-9][0-9]|[1-9]?[0-9])|[a-zA-Z0-9-]*[a-zA-Z0-9]: (?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]| \\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])$ ''' if re.match(pattern, email, re.VERBOSE): return True return False # Problem: Overly complex for 99% of use cases # Hard to understand, debug, or modify
# Code Smell: Overly complex solution def validate_email(email): # Complex regex that's hard to understand and maintain import re # RFC 5322 compliant email regex (simplified version) pattern = r''' ^(?:[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*| "(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]| \\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@ (?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)*[a-zA-Z0-9] (?:[a-zA-Z0-9-]*[a-zA-Z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])| 1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])| 1[0-9][0-9]|[1-9]?[0-9])|[a-zA-Z0-9-]*[a-zA-Z0-9]: (?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]| \\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])$ ''' if re.match(pattern, email, re.VERBOSE): return True return False # Problem: Overly complex for 99% of use cases # Hard to understand, debug, or modify
# Clean: Simple solution that works for most cases def validate_email(email): # Simple check that handles 99% of real-world cases if not email or '@' not in email: return False local, domain = email.rsplit('@', 1) # Basic checks if not local or not domain: return False # Check for valid characters and structure if '.' not in domain: return False # Additional simple validations if '..' in email or email.startswith('.') or email.endswith('.'): return False return True # For production use, consider: # - Using a well-tested library (e.g., email-validator) # - Sending a confirmation email (only way to truly validate) # Benefits: # - Easy to understand and maintain # - Covers most real-world cases # - Can be enhanced if needed # - Clear intent
# Clean: Simple solution that works for most cases def validate_email(email): # Simple check that handles 99% of real-world cases if not email or '@' not in email: return False local, domain = email.rsplit('@', 1) # Basic checks if not local or not domain: return False # Check for valid characters and structure if '.' not in domain: return False # Additional simple validations if '..' in email or email.startswith('.') or email.endswith('.'): return False return True # For production use, consider: # - Using a well-tested library (e.g., email-validator) # - Sending a confirmation email (only way to truly validate) # Benefits: # - Easy to understand and maintain # - Covers most real-world cases # - Can be enhanced if needed # - Clear intent
# Code Smell: Feature Envy class Customer: def __init__(self, name, address, phone): self.name = name self.address = address self.phone = phone class InvoiceService: def generate_customer_summary(self, customer): # This method is more interested in Customer data than its own full_address = f"{customer.address.street}, {customer.address.city}" contact_info = f"{customer.name} - {customer.phone}" formatted_address = f"Address: {full_address}" return f"{contact_info}\n{formatted_address}" def format_customer_for_display(self, customer): # Another method obsessed with Customer's internals return f"{customer.name.upper()} ({customer.address.city})" # Problem: InvoiceService knows too much about Customer structure # Methods belong more to Customer than InvoiceService
# Code Smell: Feature Envy class Customer: def __init__(self, name, address, phone): self.name = name self.address = address self.phone = phone class InvoiceService: def generate_customer_summary(self, customer): # This method is more interested in Customer data than its own full_address = f"{customer.address.street}, {customer.address.city}" contact_info = f"{customer.name} - {customer.phone}" formatted_address = f"Address: {full_address}" return f"{contact_info}\n{formatted_address}" def format_customer_for_display(self, customer): # Another method obsessed with Customer's internals return f"{customer.name.upper()} ({customer.address.city})" # Problem: InvoiceService knows too much about Customer structure # Methods belong more to Customer than InvoiceService
# Clean: Move Methods to Appropriate Class class Customer: def __init__(self, name, address, phone): self.name = name self.address = address self.phone = phone def get_contact_summary(self): # Method belongs here - it knows Customer's structure full_address = f"{self.address.street}, {self.address.city}" contact_info = f"{self.name} - {self.phone}" formatted_address = f"Address: {full_address}" return f"{contact_info}\n{formatted_address}" def get_display_name(self): return f"{self.name.upper()} ({self.address.city})" class InvoiceService: def generate_customer_summary(self, customer): # Now delegates to Customer - proper encapsulation return customer.get_contact_summary() def format_customer_for_display(self, customer): return customer.get_display_name() def calculate_invoice_total(self, line_items): # InvoiceService focuses on its own responsibilities return sum(item.price * item.quantity for item in line_items) # Benefits: Better encapsulation, methods are where they belong # Each class focuses on its own data and behavior
# Clean: Move Methods to Appropriate Class class Customer: def __init__(self, name, address, phone): self.name = name self.address = address self.phone = phone def get_contact_summary(self): # Method belongs here - it knows Customer's structure full_address = f"{self.address.street}, {self.address.city}" contact_info = f"{self.name} - {self.phone}" formatted_address = f"Address: {full_address}" return f"{contact_info}\n{formatted_address}" def get_display_name(self): return f"{self.name.upper()} ({self.address.city})" class InvoiceService: def generate_customer_summary(self, customer): # Now delegates to Customer - proper encapsulation return customer.get_contact_summary() def format_customer_for_display(self, customer): return customer.get_display_name() def calculate_invoice_total(self, line_items): # InvoiceService focuses on its own responsibilities return sum(item.price * item.quantity for item in line_items) # Benefits: Better encapsulation, methods are where they belong # Each class focuses on its own data and behavior
# Clean: Move Methods to Appropriate Class class Customer: def __init__(self, name, address, phone): self.name = name self.address = address self.phone = phone def get_contact_summary(self): # Method belongs here - it knows Customer's structure full_address = f"{self.address.street}, {self.address.city}" contact_info = f"{self.name} - {self.phone}" formatted_address = f"Address: {full_address}" return f"{contact_info}\n{formatted_address}" def get_display_name(self): return f"{self.name.upper()} ({self.address.city})" class InvoiceService: def generate_customer_summary(self, customer): # Now delegates to Customer - proper encapsulation return customer.get_contact_summary() def format_customer_for_display(self, customer): return customer.get_display_name() def calculate_invoice_total(self, line_items): # InvoiceService focuses on its own responsibilities return sum(item.price * item.quantity for item in line_items) # Benefits: Better encapsulation, methods are where they belong # Each class focuses on its own data and behavior
A class should have only one responsibility and one reason to change.
# Violates SRP - Multiple responsibilities class UserManager: def __init__(self): self.users = [] def add_user(self, user): # User management responsibility self.users.append(user) def send_email(self, user, message): # Email responsibility - different reason to change! print(f"Sending email to {user.email}: {message}") def save_to_database(self, user): # Persistence responsibility - another reason to change! print(f"Saving {user.name} to database") def generate_report(self): # Reporting responsibility - yet another reason to change! return f"Total users: {len(self.users)}" # Problem: Changes to email system, database, or reporting # all require modifying this class
# Violates SRP - Multiple responsibilities class UserManager: def __init__(self): self.users = [] def add_user(self, user): # User management responsibility self.users.append(user) def send_email(self, user, message): # Email responsibility - different reason to change! print(f"Sending email to {user.email}: {message}") def save_to_database(self, user): # Persistence responsibility - another reason to change! print(f"Saving {user.name} to database") def generate_report(self): # Reporting responsibility - yet another reason to change! return f"Total users: {len(self.users)}" # Problem: Changes to email system, database, or reporting # all require modifying this class
# Violates SRP - Multiple responsibilities class UserManager: def __init__(self): self.users = [] def add_user(self, user): # User management responsibility self.users.append(user) def send_email(self, user, message): # Email responsibility - different reason to change! print(f"Sending email to {user.email}: {message}") def save_to_database(self, user): # Persistence responsibility - another reason to change! print(f"Saving {user.name} to database") def generate_report(self): # Reporting responsibility - yet another reason to change! return f"Total users: {len(self.users)}" # Problem: Changes to email system, database, or reporting # all require modifying this class
# Follows SRP - Single responsibility per class class UserManager: def __init__(self): self.users = [] def add_user(self, user): self.users.append(user) class EmailService: def send_email(self, user, message): print(f"Sending email to {user.email}: {message}") class UserRepository: def save(self, user): print(f"Saving {user.name} to database") class ReportGenerator: def generate_user_report(self, users): return f"Total users: {len(users)}" # Usage with composition user_manager = UserManager() email_service = EmailService() user_repo = UserRepository() report_gen = ReportGenerator() # Each class has one reason to change # Easy to test, modify, and extend independently
# Follows SRP - Single responsibility per class class UserManager: def __init__(self): self.users = [] def add_user(self, user): self.users.append(user) class EmailService: def send_email(self, user, message): print(f"Sending email to {user.email}: {message}") class UserRepository: def save(self, user): print(f"Saving {user.name} to database") class ReportGenerator: def generate_user_report(self, users): return f"Total users: {len(users)}" # Usage with composition user_manager = UserManager() email_service = EmailService() user_repo = UserRepository() report_gen = ReportGenerator() # Each class has one reason to change # Easy to test, modify, and extend independently
# Follows SRP - Single responsibility per class class UserManager: def __init__(self): self.users = [] def add_user(self, user): self.users.append(user) class EmailService: def send_email(self, user, message): print(f"Sending email to {user.email}: {message}") class UserRepository: def save(self, user): print(f"Saving {user.name} to database") class ReportGenerator: def generate_user_report(self, users): return f"Total users: {len(users)}" # Usage with composition user_manager = UserManager() email_service = EmailService() user_repo = UserRepository() report_gen = ReportGenerator() # Each class has one reason to change # Easy to test, modify, and extend independently
Software entities should be open for extension but closed for modification. You should be able to add new functionality without changing existing code.
# Violates OCP - Must modify existing code for new shapes class AreaCalculator: def calculate_area(self, shapes): total_area = 0 for shape in shapes: if shape.type == "rectangle": total_area += shape.width * shape.height elif shape.type == "circle": total_area += 3.14159 * shape.radius ** 2 elif shape.type == "triangle": total_area += 0.5 * shape.base * shape.height # Adding new shape requires modifying this method! # elif shape.type == "pentagon": # total_area += pentagon_area_formula(shape) return total_area # Problem: Every new shape requires modifying AreaCalculator # Risk of breaking existing functionality
# Violates OCP - Must modify existing code for new shapes class AreaCalculator: def calculate_area(self, shapes): total_area = 0 for shape in shapes: if shape.type == "rectangle": total_area += shape.width * shape.height elif shape.type == "circle": total_area += 3.14159 * shape.radius ** 2 elif shape.type == "triangle": total_area += 0.5 * shape.base * shape.height # Adding new shape requires modifying this method! # elif shape.type == "pentagon": # total_area += pentagon_area_formula(shape) return total_area # Problem: Every new shape requires modifying AreaCalculator # Risk of breaking existing functionality
# Violates OCP - Must modify existing code for new shapes class AreaCalculator: def calculate_area(self, shapes): total_area = 0 for shape in shapes: if shape.type == "rectangle": total_area += shape.width * shape.height elif shape.type == "circle": total_area += 3.14159 * shape.radius ** 2 elif shape.type == "triangle": total_area += 0.5 * shape.base * shape.height # Adding new shape requires modifying this method! # elif shape.type == "pentagon": # total_area += pentagon_area_formula(shape) return total_area # Problem: Every new shape requires modifying AreaCalculator # Risk of breaking existing functionality
# Follows OCP - Open for extension, closed for modification from abc import ABC, abstractmethod class Shape(ABC): @abstractmethod def area(self): pass class Rectangle(Shape): def __init__(self, width, height): self.width = width self.height = height def area(self): return self.width * self.height class Circle(Shape): def __init__(self, radius): self.radius = radius def area(self): return 3.14159 * self.radius ** 2 class AreaCalculator: def calculate_area(self, shapes): return sum(shape.area() for shape in shapes) # Adding new shapes doesn't require changing existing code class Triangle(Shape): def __init__(self, base, height): self.base = base self.height = height def area(self): return 0.5 * self.base * self.height # AreaCalculator works with new shapes without modification!
# Follows OCP - Open for extension, closed for modification from abc import ABC, abstractmethod class Shape(ABC): @abstractmethod def area(self): pass class Rectangle(Shape): def __init__(self, width, height): self.width = width self.height = height def area(self): return self.width * self.height class Circle(Shape): def __init__(self, radius): self.radius = radius def area(self): return 3.14159 * self.radius ** 2 class AreaCalculator: def calculate_area(self, shapes): return sum(shape.area() for shape in shapes) # Adding new shapes doesn't require changing existing code class Triangle(Shape): def __init__(self, base, height): self.base = base self.height = height def area(self): return 0.5 * self.base * self.height # AreaCalculator works with new shapes without modification!
# Follows OCP - Open for extension, closed for modification from abc import ABC, abstractmethod class Shape(ABC): @abstractmethod def area(self): pass class Rectangle(Shape): def __init__(self, width, height): self.width = width self.height = height def area(self): return self.width * self.height class Circle(Shape): def __init__(self, radius): self.radius = radius def area(self): return 3.14159 * self.radius ** 2 class AreaCalculator: def calculate_area(self, shapes): return sum(shape.area() for shape in shapes) # Adding new shapes doesn't require changing existing code class Triangle(Shape): def __init__(self, base, height): self.base = base self.height = height def area(self): return 0.5 * self.base * self.height # AreaCalculator works with new shapes without modification!
# Follows OCP - Open for extension, closed for modification from abc import ABC, abstractmethod class Shape(ABC): @abstractmethod def area(self): pass class Rectangle(Shape): def __init__(self, width, height): self.width = width self.height = height def area(self): return self.width * self.height class Circle(Shape): def __init__(self, radius): self.radius = radius def area(self): return 3.14159 * self.radius ** 2 class AreaCalculator: def calculate_area(self, shapes): return sum(shape.area() for shape in shapes) # Adding new shapes doesn't require changing existing code class Triangle(Shape): def __init__(self, base, height): self.base = base self.height = height def area(self): return 0.5 * self.base * self.height # AreaCalculator works with new shapes without modification!
# Follows OCP - Open for extension, closed for modification from abc import ABC, abstractmethod class Shape(ABC): @abstractmethod def area(self): pass class Rectangle(Shape): def __init__(self, width, height): self.width = width self.height = height def area(self): return self.width * self.height class Circle(Shape): def __init__(self, radius): self.radius = radius def area(self): return 3.14159 * self.radius ** 2 class AreaCalculator: def calculate_area(self, shapes): return sum(shape.area() for shape in shapes) # Adding new shapes doesn't require changing existing code class Triangle(Shape): def __init__(self, base, height): self.base = base self.height = height def area(self): return 0.5 * self.base * self.height # AreaCalculator works with new shapes without modification!
# Follows OCP - Open for extension, closed for modification from abc import ABC, abstractmethod class Shape(ABC): @abstractmethod def area(self): pass class Rectangle(Shape): def __init__(self, width, height): self.width = width self.height = height def area(self): return self.width * self.height class Circle(Shape): def __init__(self, radius): self.radius = radius def area(self): return 3.14159 * self.radius ** 2 class AreaCalculator: def calculate_area(self, shapes): return sum(shape.area() for shape in shapes) # Adding new shapes doesn't require changing existing code class Triangle(Shape): def __init__(self, base, height): self.base = base self.height = height def area(self): return 0.5 * self.base * self.height # AreaCalculator works with new shapes without modification!
Objects of a superclass should be replaceable with objects of its subclasses without breaking the program.
# Violates LSP - Penguin changes expected behavior class Bird: def fly(self): print("Flying high in the sky") def make_sound(self): print("Chirp chirp") class Penguin(Bird): def fly(self): # Violates LSP - changes expected behavior raise Exception("Penguins can't fly!") def make_sound(self): print("Squawk") # Problem: Code breaks when substituting subclass def make_bird_fly(bird: Bird): bird.fly() # Works with Bird, breaks with Penguin! # This will crash! penguin = Penguin() make_bird_fly(penguin) # Exception: Penguins can't fly!
# Violates LSP - Penguin changes expected behavior class Bird: def fly(self): print("Flying high in the sky") def make_sound(self): print("Chirp chirp") class Penguin(Bird): def fly(self): # Violates LSP - changes expected behavior raise Exception("Penguins can't fly!") def make_sound(self): print("Squawk") # Problem: Code breaks when substituting subclass def make_bird_fly(bird: Bird): bird.fly() # Works with Bird, breaks with Penguin! # This will crash! penguin = Penguin() make_bird_fly(penguin) # Exception: Penguins can't fly!
# Violates LSP - Penguin changes expected behavior class Bird: def fly(self): print("Flying high in the sky") def make_sound(self): print("Chirp chirp") class Penguin(Bird): def fly(self): # Violates LSP - changes expected behavior raise Exception("Penguins can't fly!") def make_sound(self): print("Squawk") # Problem: Code breaks when substituting subclass def make_bird_fly(bird: Bird): bird.fly() # Works with Bird, breaks with Penguin! # This will crash! penguin = Penguin() make_bird_fly(penguin) # Exception: Penguins can't fly!
# Violates LSP - Penguin changes expected behavior class Bird: def fly(self): print("Flying high in the sky") def make_sound(self): print("Chirp chirp") class Penguin(Bird): def fly(self): # Violates LSP - changes expected behavior raise Exception("Penguins can't fly!") def make_sound(self): print("Squawk") # Problem: Code breaks when substituting subclass def make_bird_fly(bird: Bird): bird.fly() # Works with Bird, breaks with Penguin! # This will crash! penguin = Penguin() make_bird_fly(penguin) # Exception: Penguins can't fly!
# Violates LSP - Penguin changes expected behavior class Bird: def fly(self): print("Flying high in the sky") def make_sound(self): print("Chirp chirp") class Penguin(Bird): def fly(self): # Violates LSP - changes expected behavior raise Exception("Penguins can't fly!") def make_sound(self): print("Squawk") # Problem: Code breaks when substituting subclass def make_bird_fly(bird: Bird): bird.fly() # Works with Bird, breaks with Penguin! # This will crash! penguin = Penguin() make_bird_fly(penguin) # Exception: Penguins can't fly!
# Follows LSP - Subclasses are substitutable from abc import ABC, abstractmethod class Bird(ABC): @abstractmethod def make_sound(self): pass class FlyingBird(Bird): def fly(self): print("Flying high in the sky") def make_sound(self): print("Chirp chirp") class FlightlessBird(Bird): def walk(self): print("Walking on the ground") def make_sound(self): print("Squawk") class Sparrow(FlyingBird): pass class Penguin(FlightlessBird): def swim(self): print("Swimming gracefully") # Code works correctly with all substitutions def make_bird_sound(bird: Bird): bird.make_sound() # Works with all bird types def make_flying_bird_fly(bird: FlyingBird): bird.fly() # Only accepts birds that can fly # All substitutions work as expected sparrow = Sparrow() penguin = Penguin() make_bird_sound(sparrow) # Works make_bird_sound(penguin) # Works make_flying_bird_fly(sparrow) # Works # make_flying_bird_fly(penguin) # Type error - prevented at compile time
# Follows LSP - Subclasses are substitutable from abc import ABC, abstractmethod class Bird(ABC): @abstractmethod def make_sound(self): pass class FlyingBird(Bird): def fly(self): print("Flying high in the sky") def make_sound(self): print("Chirp chirp") class FlightlessBird(Bird): def walk(self): print("Walking on the ground") def make_sound(self): print("Squawk") class Sparrow(FlyingBird): pass class Penguin(FlightlessBird): def swim(self): print("Swimming gracefully") # Code works correctly with all substitutions def make_bird_sound(bird: Bird): bird.make_sound() # Works with all bird types def make_flying_bird_fly(bird: FlyingBird): bird.fly() # Only accepts birds that can fly # All substitutions work as expected sparrow = Sparrow() penguin = Penguin() make_bird_sound(sparrow) # Works make_bird_sound(penguin) # Works make_flying_bird_fly(sparrow) # Works # make_flying_bird_fly(penguin) # Type error - prevented at compile time
# Follows LSP - Subclasses are substitutable from abc import ABC, abstractmethod class Bird(ABC): @abstractmethod def make_sound(self): pass class FlyingBird(Bird): def fly(self): print("Flying high in the sky") def make_sound(self): print("Chirp chirp") class FlightlessBird(Bird): def walk(self): print("Walking on the ground") def make_sound(self): print("Squawk") class Sparrow(FlyingBird): pass class Penguin(FlightlessBird): def swim(self): print("Swimming gracefully") # Code works correctly with all substitutions def make_bird_sound(bird: Bird): bird.make_sound() # Works with all bird types def make_flying_bird_fly(bird: FlyingBird): bird.fly() # Only accepts birds that can fly # All substitutions work as expected sparrow = Sparrow() penguin = Penguin() make_bird_sound(sparrow) # Works make_bird_sound(penguin) # Works make_flying_bird_fly(sparrow) # Works # make_flying_bird_fly(penguin) # Type error - prevented at compile time
# Follows LSP - Subclasses are substitutable from abc import ABC, abstractmethod class Bird(ABC): @abstractmethod def make_sound(self): pass class FlyingBird(Bird): def fly(self): print("Flying high in the sky") def make_sound(self): print("Chirp chirp") class FlightlessBird(Bird): def walk(self): print("Walking on the ground") def make_sound(self): print("Squawk") class Sparrow(FlyingBird): pass class Penguin(FlightlessBird): def swim(self): print("Swimming gracefully") # Code works correctly with all substitutions def make_bird_sound(bird: Bird): bird.make_sound() # Works with all bird types def make_flying_bird_fly(bird: FlyingBird): bird.fly() # Only accepts birds that can fly # All substitutions work as expected sparrow = Sparrow() penguin = Penguin() make_bird_sound(sparrow) # Works make_bird_sound(penguin) # Works make_flying_bird_fly(sparrow) # Works # make_flying_bird_fly(penguin) # Type error - prevented at compile time
# Follows LSP - Subclasses are substitutable from abc import ABC, abstractmethod class Bird(ABC): @abstractmethod def make_sound(self): pass class FlyingBird(Bird): def fly(self): print("Flying high in the sky") def make_sound(self): print("Chirp chirp") class FlightlessBird(Bird): def walk(self): print("Walking on the ground") def make_sound(self): print("Squawk") class Sparrow(FlyingBird): pass class Penguin(FlightlessBird): def swim(self): print("Swimming gracefully") # Code works correctly with all substitutions def make_bird_sound(bird: Bird): bird.make_sound() # Works with all bird types def make_flying_bird_fly(bird: FlyingBird): bird.fly() # Only accepts birds that can fly # All substitutions work as expected sparrow = Sparrow() penguin = Penguin() make_bird_sound(sparrow) # Works make_bird_sound(penguin) # Works make_flying_bird_fly(sparrow) # Works # make_flying_bird_fly(penguin) # Type error - prevented at compile time
Clients should not be forced to depend on interfaces they do not use.
# Violates ISP - Fat interface with unused methods from abc import ABC, abstractmethod class Worker(ABC): @abstractmethod def work(self): pass @abstractmethod def eat(self): pass @abstractmethod def sleep(self): pass class HumanWorker(Worker): def work(self): print("Human working") def eat(self): print("Human eating") def sleep(self): print("Human sleeping") class RobotWorker(Worker): def work(self): print("Robot working") def eat(self): # Forced to implement unused method! raise NotImplementedError("Robots don't eat") def sleep(self): # Forced to implement unused method! raise NotImplementedError("Robots don't sleep") # Problem: RobotWorker forced to implement irrelevant methods
# Violates ISP - Fat interface with unused methods from abc import ABC, abstractmethod class Worker(ABC): @abstractmethod def work(self): pass @abstractmethod def eat(self): pass @abstractmethod def sleep(self): pass class HumanWorker(Worker): def work(self): print("Human working") def eat(self): print("Human eating") def sleep(self): print("Human sleeping") class RobotWorker(Worker): def work(self): print("Robot working") def eat(self): # Forced to implement unused method! raise NotImplementedError("Robots don't eat") def sleep(self): # Forced to implement unused method! raise NotImplementedError("Robots don't sleep") # Problem: RobotWorker forced to implement irrelevant methods
# Violates ISP - Fat interface with unused methods from abc import ABC, abstractmethod class Worker(ABC): @abstractmethod def work(self): pass @abstractmethod def eat(self): pass @abstractmethod def sleep(self): pass class HumanWorker(Worker): def work(self): print("Human working") def eat(self): print("Human eating") def sleep(self): print("Human sleeping") class RobotWorker(Worker): def work(self): print("Robot working") def eat(self): # Forced to implement unused method! raise NotImplementedError("Robots don't eat") def sleep(self): # Forced to implement unused method! raise NotImplementedError("Robots don't sleep") # Problem: RobotWorker forced to implement irrelevant methods
# Violates ISP - Fat interface with unused methods from abc import ABC, abstractmethod class Worker(ABC): @abstractmethod def work(self): pass @abstractmethod def eat(self): pass @abstractmethod def sleep(self): pass class HumanWorker(Worker): def work(self): print("Human working") def eat(self): print("Human eating") def sleep(self): print("Human sleeping") class RobotWorker(Worker): def work(self): print("Robot working") def eat(self): # Forced to implement unused method! raise NotImplementedError("Robots don't eat") def sleep(self): # Forced to implement unused method! raise NotImplementedError("Robots don't sleep") # Problem: RobotWorker forced to implement irrelevant methods
# Violates ISP - Fat interface with unused methods from abc import ABC, abstractmethod class Worker(ABC): @abstractmethod def work(self): pass @abstractmethod def eat(self): pass @abstractmethod def sleep(self): pass class HumanWorker(Worker): def work(self): print("Human working") def eat(self): print("Human eating") def sleep(self): print("Human sleeping") class RobotWorker(Worker): def work(self): print("Robot working") def eat(self): # Forced to implement unused method! raise NotImplementedError("Robots don't eat") def sleep(self): # Forced to implement unused method! raise NotImplementedError("Robots don't sleep") # Problem: RobotWorker forced to implement irrelevant methods
# Follows ISP - Segregated interfaces from abc import ABC, abstractmethod class Workable(ABC): @abstractmethod def work(self): pass class Eatable(ABC): @abstractmethod def eat(self): pass class Sleepable(ABC): @abstractmethod def sleep(self): pass class HumanWorker(Workable, Eatable, Sleepable): def work(self): print("Human working") def eat(self): print("Human eating") def sleep(self): print("Human sleeping") class RobotWorker(Workable): def work(self): print("Robot working") # Only implements what it needs! # Manager works with specific interfaces class WorkManager: def manage_work(self, worker: Workable): worker.work() def manage_lunch(self, worker: Eatable): worker.eat() # Clean usage - no forced implementations work_manager = WorkManager() human = HumanWorker() robot = RobotWorker() work_manager.manage_work(human) # Works work_manager.manage_work(robot) # Works work_manager.manage_lunch(human) # Works # work_manager.manage_lunch(robot) # Type error - prevented!
# Follows ISP - Segregated interfaces from abc import ABC, abstractmethod class Workable(ABC): @abstractmethod def work(self): pass class Eatable(ABC): @abstractmethod def eat(self): pass class Sleepable(ABC): @abstractmethod def sleep(self): pass class HumanWorker(Workable, Eatable, Sleepable): def work(self): print("Human working") def eat(self): print("Human eating") def sleep(self): print("Human sleeping") class RobotWorker(Workable): def work(self): print("Robot working") # Only implements what it needs! # Manager works with specific interfaces class WorkManager: def manage_work(self, worker: Workable): worker.work() def manage_lunch(self, worker: Eatable): worker.eat() # Clean usage - no forced implementations work_manager = WorkManager() human = HumanWorker() robot = RobotWorker() work_manager.manage_work(human) # Works work_manager.manage_work(robot) # Works work_manager.manage_lunch(human) # Works # work_manager.manage_lunch(robot) # Type error - prevented!
# Follows ISP - Segregated interfaces from abc import ABC, abstractmethod class Workable(ABC): @abstractmethod def work(self): pass class Eatable(ABC): @abstractmethod def eat(self): pass class Sleepable(ABC): @abstractmethod def sleep(self): pass class HumanWorker(Workable, Eatable, Sleepable): def work(self): print("Human working") def eat(self): print("Human eating") def sleep(self): print("Human sleeping") class RobotWorker(Workable): def work(self): print("Robot working") # Only implements what it needs! # Manager works with specific interfaces class WorkManager: def manage_work(self, worker: Workable): worker.work() def manage_lunch(self, worker: Eatable): worker.eat() # Clean usage - no forced implementations work_manager = WorkManager() human = HumanWorker() robot = RobotWorker() work_manager.manage_work(human) # Works work_manager.manage_work(robot) # Works work_manager.manage_lunch(human) # Works # work_manager.manage_lunch(robot) # Type error - prevented!
# Follows ISP - Segregated interfaces from abc import ABC, abstractmethod class Workable(ABC): @abstractmethod def work(self): pass class Eatable(ABC): @abstractmethod def eat(self): pass class Sleepable(ABC): @abstractmethod def sleep(self): pass class HumanWorker(Workable, Eatable, Sleepable): def work(self): print("Human working") def eat(self): print("Human eating") def sleep(self): print("Human sleeping") class RobotWorker(Workable): def work(self): print("Robot working") # Only implements what it needs! # Manager works with specific interfaces class WorkManager: def manage_work(self, worker: Workable): worker.work() def manage_lunch(self, worker: Eatable): worker.eat() # Clean usage - no forced implementations work_manager = WorkManager() human = HumanWorker() robot = RobotWorker() work_manager.manage_work(human) # Works work_manager.manage_work(robot) # Works work_manager.manage_lunch(human) # Works # work_manager.manage_lunch(robot) # Type error - prevented!
# Follows ISP - Segregated interfaces from abc import ABC, abstractmethod class Workable(ABC): @abstractmethod def work(self): pass class Eatable(ABC): @abstractmethod def eat(self): pass class Sleepable(ABC): @abstractmethod def sleep(self): pass class HumanWorker(Workable, Eatable, Sleepable): def work(self): print("Human working") def eat(self): print("Human eating") def sleep(self): print("Human sleeping") class RobotWorker(Workable): def work(self): print("Robot working") # Only implements what it needs! # Manager works with specific interfaces class WorkManager: def manage_work(self, worker: Workable): worker.work() def manage_lunch(self, worker: Eatable): worker.eat() # Clean usage - no forced implementations work_manager = WorkManager() human = HumanWorker() robot = RobotWorker() work_manager.manage_work(human) # Works work_manager.manage_work(robot) # Works work_manager.manage_lunch(human) # Works # work_manager.manage_lunch(robot) # Type error - prevented!
# Follows ISP - Segregated interfaces from abc import ABC, abstractmethod class Workable(ABC): @abstractmethod def work(self): pass class Eatable(ABC): @abstractmethod def eat(self): pass class Sleepable(ABC): @abstractmethod def sleep(self): pass class HumanWorker(Workable, Eatable, Sleepable): def work(self): print("Human working") def eat(self): print("Human eating") def sleep(self): print("Human sleeping") class RobotWorker(Workable): def work(self): print("Robot working") # Only implements what it needs! # Manager works with specific interfaces class WorkManager: def manage_work(self, worker: Workable): worker.work() def manage_lunch(self, worker: Eatable): worker.eat() # Clean usage - no forced implementations work_manager = WorkManager() human = HumanWorker() robot = RobotWorker() work_manager.manage_work(human) # Works work_manager.manage_work(robot) # Works work_manager.manage_lunch(human) # Works # work_manager.manage_lunch(robot) # Type error - prevented!
# Follows ISP - Segregated interfaces from abc import ABC, abstractmethod class Workable(ABC): @abstractmethod def work(self): pass class Eatable(ABC): @abstractmethod def eat(self): pass class Sleepable(ABC): @abstractmethod def sleep(self): pass class HumanWorker(Workable, Eatable, Sleepable): def work(self): print("Human working") def eat(self): print("Human eating") def sleep(self): print("Human sleeping") class RobotWorker(Workable): def work(self): print("Robot working") # Only implements what it needs! # Manager works with specific interfaces class WorkManager: def manage_work(self, worker: Workable): worker.work() def manage_lunch(self, worker: Eatable): worker.eat() # Clean usage - no forced implementations work_manager = WorkManager() human = HumanWorker() robot = RobotWorker() work_manager.manage_work(human) # Works work_manager.manage_work(robot) # Works work_manager.manage_lunch(human) # Works # work_manager.manage_lunch(robot) # Type error - prevented!
# Follows ISP - Segregated interfaces from abc import ABC, abstractmethod class Workable(ABC): @abstractmethod def work(self): pass class Eatable(ABC): @abstractmethod def eat(self): pass class Sleepable(ABC): @abstractmethod def sleep(self): pass class HumanWorker(Workable, Eatable, Sleepable): def work(self): print("Human working") def eat(self): print("Human eating") def sleep(self): print("Human sleeping") class RobotWorker(Workable): def work(self): print("Robot working") # Only implements what it needs! # Manager works with specific interfaces class WorkManager: def manage_work(self, worker: Workable): worker.work() def manage_lunch(self, worker: Eatable): worker.eat() # Clean usage - no forced implementations work_manager = WorkManager() human = HumanWorker() robot = RobotWorker() work_manager.manage_work(human) # Works work_manager.manage_work(robot) # Works work_manager.manage_lunch(human) # Works # work_manager.manage_lunch(robot) # Type error - prevented!
High-level modules should not depend on low-level modules. Both should depend on abstractions.
# Violates DIP - High-level depends on low-level concrete classes class MySQLDatabase: def save(self, data): print(f"Saving to MySQL: {data}") class EmailService: def send(self, message): print(f"Sending email: {message}") class OrderProcessor: def __init__(self): # High-level module depends on concrete low-level modules self.database = MySQLDatabase() self.email_service = EmailService() def process_order(self, order): self.database.save(order) self.email_service.send(f"Order {order.id} processed") # Problem: Hard to change database or email provider # Hard to test with mocks # Tightly coupled to specific implementations
# Violates DIP - High-level depends on low-level concrete classes class MySQLDatabase: def save(self, data): print(f"Saving to MySQL: {data}") class EmailService: def send(self, message): print(f"Sending email: {message}") class OrderProcessor: def __init__(self): # High-level module depends on concrete low-level modules self.database = MySQLDatabase() self.email_service = EmailService() def process_order(self, order): self.database.save(order) self.email_service.send(f"Order {order.id} processed") # Problem: Hard to change database or email provider # Hard to test with mocks # Tightly coupled to specific implementations
# Violates DIP - High-level depends on low-level concrete classes class MySQLDatabase: def save(self, data): print(f"Saving to MySQL: {data}") class EmailService: def send(self, message): print(f"Sending email: {message}") class OrderProcessor: def __init__(self): # High-level module depends on concrete low-level modules self.database = MySQLDatabase() self.email_service = EmailService() def process_order(self, order): self.database.save(order) self.email_service.send(f"Order {order.id} processed") # Problem: Hard to change database or email provider # Hard to test with mocks # Tightly coupled to specific implementations
# Follows DIP - Both depend on abstractions from abc import ABC, abstractmethod class Database(ABC): @abstractmethod def save(self, data): pass class NotificationService(ABC): @abstractmethod def send(self, message): pass class OrderProcessor: def __init__(self, database: Database, notification: NotificationService): # High-level module depends on abstractions self.database = database self.notification = notification def process_order(self, order): self.database.save(order) self.notification.send(f"Order {order.id} processed") # Low-level modules implement abstractions class MySQLDatabase(Database): def save(self, data): print(f"Saving to MySQL: {data}") class PostgreSQLDatabase(Database): def save(self, data): print(f"Saving to PostgreSQL: {data}") class EmailService(NotificationService): def send(self, message): print(f"Sending email: {message}") class SMSService(NotificationService): def send(self, message): print(f"Sending SMS: {message}") # Easy to switch implementations mysql_db = MySQLDatabase() email_service = EmailService() processor = OrderProcessor(mysql_db, email_service) # Or use different implementations postgres_db = PostgreSQLDatabase() sms_service = SMSService() processor2 = OrderProcessor(postgres_db, sms_service)
# Follows DIP - Both depend on abstractions from abc import ABC, abstractmethod class Database(ABC): @abstractmethod def save(self, data): pass class NotificationService(ABC): @abstractmethod def send(self, message): pass class OrderProcessor: def __init__(self, database: Database, notification: NotificationService): # High-level module depends on abstractions self.database = database self.notification = notification def process_order(self, order): self.database.save(order) self.notification.send(f"Order {order.id} processed") # Low-level modules implement abstractions class MySQLDatabase(Database): def save(self, data): print(f"Saving to MySQL: {data}") class PostgreSQLDatabase(Database): def save(self, data): print(f"Saving to PostgreSQL: {data}") class EmailService(NotificationService): def send(self, message): print(f"Sending email: {message}") class SMSService(NotificationService): def send(self, message): print(f"Sending SMS: {message}") # Easy to switch implementations mysql_db = MySQLDatabase() email_service = EmailService() processor = OrderProcessor(mysql_db, email_service) # Or use different implementations postgres_db = PostgreSQLDatabase() sms_service = SMSService() processor2 = OrderProcessor(postgres_db, sms_service)
# Follows DIP - Both depend on abstractions from abc import ABC, abstractmethod class Database(ABC): @abstractmethod def save(self, data): pass class NotificationService(ABC): @abstractmethod def send(self, message): pass class OrderProcessor: def __init__(self, database: Database, notification: NotificationService): # High-level module depends on abstractions self.database = database self.notification = notification def process_order(self, order): self.database.save(order) self.notification.send(f"Order {order.id} processed") # Low-level modules implement abstractions class MySQLDatabase(Database): def save(self, data): print(f"Saving to MySQL: {data}") class PostgreSQLDatabase(Database): def save(self, data): print(f"Saving to PostgreSQL: {data}") class EmailService(NotificationService): def send(self, message): print(f"Sending email: {message}") class SMSService(NotificationService): def send(self, message): print(f"Sending SMS: {message}") # Easy to switch implementations mysql_db = MySQLDatabase() email_service = EmailService() processor = OrderProcessor(mysql_db, email_service) # Or use different implementations postgres_db = PostgreSQLDatabase() sms_service = SMSService() processor2 = OrderProcessor(postgres_db, sms_service)
# Follows DIP - Both depend on abstractions from abc import ABC, abstractmethod class Database(ABC): @abstractmethod def save(self, data): pass class NotificationService(ABC): @abstractmethod def send(self, message): pass class OrderProcessor: def __init__(self, database: Database, notification: NotificationService): # High-level module depends on abstractions self.database = database self.notification = notification def process_order(self, order): self.database.save(order) self.notification.send(f"Order {order.id} processed") # Low-level modules implement abstractions class MySQLDatabase(Database): def save(self, data): print(f"Saving to MySQL: {data}") class PostgreSQLDatabase(Database): def save(self, data): print(f"Saving to PostgreSQL: {data}") class EmailService(NotificationService): def send(self, message): print(f"Sending email: {message}") class SMSService(NotificationService): def send(self, message): print(f"Sending SMS: {message}") # Easy to switch implementations mysql_db = MySQLDatabase() email_service = EmailService() processor = OrderProcessor(mysql_db, email_service) # Or use different implementations postgres_db = PostgreSQLDatabase() sms_service = SMSService() processor2 = OrderProcessor(postgres_db, sms_service)
# Follows DIP - Both depend on abstractions from abc import ABC, abstractmethod class Database(ABC): @abstractmethod def save(self, data): pass class NotificationService(ABC): @abstractmethod def send(self, message): pass class OrderProcessor: def __init__(self, database: Database, notification: NotificationService): # High-level module depends on abstractions self.database = database self.notification = notification def process_order(self, order): self.database.save(order) self.notification.send(f"Order {order.id} processed") # Low-level modules implement abstractions class MySQLDatabase(Database): def save(self, data): print(f"Saving to MySQL: {data}") class PostgreSQLDatabase(Database): def save(self, data): print(f"Saving to PostgreSQL: {data}") class EmailService(NotificationService): def send(self, message): print(f"Sending email: {message}") class SMSService(NotificationService): def send(self, message): print(f"Sending SMS: {message}") # Easy to switch implementations mysql_db = MySQLDatabase() email_service = EmailService() processor = OrderProcessor(mysql_db, email_service) # Or use different implementations postgres_db = PostgreSQLDatabase() sms_service = SMSService() processor2 = OrderProcessor(postgres_db, sms_service)
# Follows DIP - Both depend on abstractions from abc import ABC, abstractmethod class Database(ABC): @abstractmethod def save(self, data): pass class NotificationService(ABC): @abstractmethod def send(self, message): pass class OrderProcessor: def __init__(self, database: Database, notification: NotificationService): # High-level module depends on abstractions self.database = database self.notification = notification def process_order(self, order): self.database.save(order) self.notification.send(f"Order {order.id} processed") # Low-level modules implement abstractions class MySQLDatabase(Database): def save(self, data): print(f"Saving to MySQL: {data}") class PostgreSQLDatabase(Database): def save(self, data): print(f"Saving to PostgreSQL: {data}") class EmailService(NotificationService): def send(self, message): print(f"Sending email: {message}") class SMSService(NotificationService): def send(self, message): print(f"Sending SMS: {message}") # Easy to switch implementations mysql_db = MySQLDatabase() email_service = EmailService() processor = OrderProcessor(mysql_db, email_service) # Or use different implementations postgres_db = PostgreSQLDatabase() sms_service = SMSService() processor2 = OrderProcessor(postgres_db, sms_service)
Use the Iterator pattern when:
class BookShelf: def __init__(self): self.books = [] def add_book(self, book): self.books.append(book) def get_books(self): return self.books # Client code has to know internal structure shelf = BookShelf() shelf.add_book("Design Patterns") shelf.add_book("Clean Code") shelf.add_book("Refactoring") # Direct access to internal list books = shelf.get_books() for i in range(len(books)): print(f"Book {i + 1}: {books[i]}") # Client can modify internal state books.clear() # This breaks encapsulation!
class BookShelf: def __init__(self): self.books = [] def add_book(self, book): self.books.append(book) def get_books(self): return self.books # Client code has to know internal structure shelf = BookShelf() shelf.add_book("Design Patterns") shelf.add_book("Clean Code") shelf.add_book("Refactoring") # Direct access to internal list books = shelf.get_books() for i in range(len(books)): print(f"Book {i + 1}: {books[i]}") # Client can modify internal state books.clear() # This breaks encapsulation!
class BookShelf: def __init__(self): self.books = [] def add_book(self, book): self.books.append(book) def get_books(self): return self.books # Client code has to know internal structure shelf = BookShelf() shelf.add_book("Design Patterns") shelf.add_book("Clean Code") shelf.add_book("Refactoring") # Direct access to internal list books = shelf.get_books() for i in range(len(books)): print(f"Book {i + 1}: {books[i]}") # Client can modify internal state books.clear() # This breaks encapsulation!
class BookShelf: def __init__(self): self._books = [] def add_book(self, book): self._books.append(book) def __iter__(self): return BookIterator(self._books) class BookIterator: def __init__(self, books): self._books = books self._index = 0 def __iter__(self): return self def __next__(self): if self._index < len(self._books): book = self._books[self._index] self._index += 1 return book raise StopIteration # Client code doesn't need to know internal structure shelf = BookShelf() shelf.add_book("Design Patterns") shelf.add_book("Clean Code") shelf.add_book("Refactoring") # Clean iteration without exposing internals for book in shelf: print(f"Book: {book}") # Using while loop with iterator manually iterator = iter(shelf) while True: try: book = next(iterator) print(f"Manual iteration: {book}") except StopIteration: break # Can't modify internal state - better encapsulation!
class BookShelf: def __init__(self): self._books = [] def add_book(self, book): self._books.append(book) def __iter__(self): return BookIterator(self._books) class BookIterator: def __init__(self, books): self._books = books self._index = 0 def __iter__(self): return self def __next__(self): if self._index < len(self._books): book = self._books[self._index] self._index += 1 return book raise StopIteration # Client code doesn't need to know internal structure shelf = BookShelf() shelf.add_book("Design Patterns") shelf.add_book("Clean Code") shelf.add_book("Refactoring") # Clean iteration without exposing internals for book in shelf: print(f"Book: {book}") # Using while loop with iterator manually iterator = iter(shelf) while True: try: book = next(iterator) print(f"Manual iteration: {book}") except StopIteration: break # Can't modify internal state - better encapsulation!
class BookShelf: def __init__(self): self._books = [] def add_book(self, book): self._books.append(book) def __iter__(self): return BookIterator(self._books) class BookIterator: def __init__(self, books): self._books = books self._index = 0 def __iter__(self): return self def __next__(self): if self._index < len(self._books): book = self._books[self._index] self._index += 1 return book raise StopIteration # Client code doesn't need to know internal structure shelf = BookShelf() shelf.add_book("Design Patterns") shelf.add_book("Clean Code") shelf.add_book("Refactoring") # Clean iteration without exposing internals for book in shelf: print(f"Book: {book}") # Using while loop with iterator manually iterator = iter(shelf) while True: try: book = next(iterator) print(f"Manual iteration: {book}") except StopIteration: break # Can't modify internal state - better encapsulation!
class BookShelf: def __init__(self): self._books = [] def add_book(self, book): self._books.append(book) def __iter__(self): return BookIterator(self._books) class BookIterator: def __init__(self, books): self._books = books self._index = 0 def __iter__(self): return self def __next__(self): if self._index < len(self._books): book = self._books[self._index] self._index += 1 return book raise StopIteration # Client code doesn't need to know internal structure shelf = BookShelf() shelf.add_book("Design Patterns") shelf.add_book("Clean Code") shelf.add_book("Refactoring") # Clean iteration without exposing internals for book in shelf: print(f"Book: {book}") # Using while loop with iterator manually iterator = iter(shelf) while True: try: book = next(iterator) print(f"Manual iteration: {book}") except StopIteration: break # Can't modify internal state - better encapsulation!
class BookShelf: def __init__(self): self._books = [] def add_book(self, book): self._books.append(book) def __iter__(self): return BookIterator(self._books) class BookIterator: def __init__(self, books): self._books = books self._index = 0 def __iter__(self): return self def __next__(self): if self._index < len(self._books): book = self._books[self._index] self._index += 1 return book raise StopIteration # Client code doesn't need to know internal structure shelf = BookShelf() shelf.add_book("Design Patterns") shelf.add_book("Clean Code") shelf.add_book("Refactoring") # Clean iteration without exposing internals for book in shelf: print(f"Book: {book}") # Using while loop with iterator manually iterator = iter(shelf) while True: try: book = next(iterator) print(f"Manual iteration: {book}") except StopIteration: break # Can't modify internal state - better encapsulation!
class TreeNode: def __init__(self, value): self.value = value self.children = [] def add_child(self, child): self.children.append(child) def traverse(self): """Tight coupling - traversal logic mixed with tree structure""" result = [] def _traverse_recursive(node): result.append(node.value) for child in node.children: _traverse_recursive(child) _traverse_recursive(self) return result # Usage root = TreeNode("A") root.add_child(TreeNode("B")) root.add_child(TreeNode("C")) root.children[0].add_child(TreeNode("D")) # Only one way to traverse - tightly coupled values = root.traverse() print(values) # ['A', 'B', 'D', 'C']
class TreeNode: def __init__(self, value): self.value = value self.children = [] def add_child(self, child): self.children.append(child) def traverse(self): """Tight coupling - traversal logic mixed with tree structure""" result = [] def _traverse_recursive(node): result.append(node.value) for child in node.children: _traverse_recursive(child) _traverse_recursive(self) return result # Usage root = TreeNode("A") root.add_child(TreeNode("B")) root.add_child(TreeNode("C")) root.children[0].add_child(TreeNode("D")) # Only one way to traverse - tightly coupled values = root.traverse() print(values) # ['A', 'B', 'D', 'C']
class TreeNode: def __init__(self, value): self.value = value self.children = [] def add_child(self, child): self.children.append(child) def traverse(self): """Tight coupling - traversal logic mixed with tree structure""" result = [] def _traverse_recursive(node): result.append(node.value) for child in node.children: _traverse_recursive(child) _traverse_recursive(self) return result # Usage root = TreeNode("A") root.add_child(TreeNode("B")) root.add_child(TreeNode("C")) root.children[0].add_child(TreeNode("D")) # Only one way to traverse - tightly coupled values = root.traverse() print(values) # ['A', 'B', 'D', 'C']
class TreeNode: def __init__(self, value): self.value = value self.children = [] def add_child(self, child): self.children.append(child) def __iter__(self): """Returns an iterator - separation of concerns""" return TreeIterator(self) class TreeIterator: def __init__(self, root): self.stack = [root] def __iter__(self): return self def __next__(self): if not self.stack: raise StopIteration current = self.stack.pop() # Add children in reverse order for left-to-right traversal for child in reversed(current.children): self.stack.append(child) return current.value # Usage root = TreeNode("A") root.add_child(TreeNode("B")) root.add_child(TreeNode("C")) root.children[0].add_child(TreeNode("D")) # Clean iteration interface for value in root: print(value) # A, B, D, C # Can also manually control iteration iterator = iter(root) print(next(iterator)) # A print(next(iterator)) # B
class TreeNode: def __init__(self, value): self.value = value self.children = [] def add_child(self, child): self.children.append(child) def __iter__(self): """Returns an iterator - separation of concerns""" return TreeIterator(self) class TreeIterator: def __init__(self, root): self.stack = [root] def __iter__(self): return self def __next__(self): if not self.stack: raise StopIteration current = self.stack.pop() # Add children in reverse order for left-to-right traversal for child in reversed(current.children): self.stack.append(child) return current.value # Usage root = TreeNode("A") root.add_child(TreeNode("B")) root.add_child(TreeNode("C")) root.children[0].add_child(TreeNode("D")) # Clean iteration interface for value in root: print(value) # A, B, D, C # Can also manually control iteration iterator = iter(root) print(next(iterator)) # A print(next(iterator)) # B
class TreeNode: def __init__(self, value): self.value = value self.children = [] def add_child(self, child): self.children.append(child) def __iter__(self): """Returns an iterator - separation of concerns""" return TreeIterator(self) class TreeIterator: def __init__(self, root): self.stack = [root] def __iter__(self): return self def __next__(self): if not self.stack: raise StopIteration current = self.stack.pop() # Add children in reverse order for left-to-right traversal for child in reversed(current.children): self.stack.append(child) return current.value # Usage root = TreeNode("A") root.add_child(TreeNode("B")) root.add_child(TreeNode("C")) root.children[0].add_child(TreeNode("D")) # Clean iteration interface for value in root: print(value) # A, B, D, C # Can also manually control iteration iterator = iter(root) print(next(iterator)) # A print(next(iterator)) # B
class TreeNode: def __init__(self, value): self.value = value self.children = [] def add_child(self, child): self.children.append(child) def __iter__(self): """Returns an iterator - separation of concerns""" return TreeIterator(self) class TreeIterator: def __init__(self, root): self.stack = [root] def __iter__(self): return self def __next__(self): if not self.stack: raise StopIteration current = self.stack.pop() # Add children in reverse order for left-to-right traversal for child in reversed(current.children): self.stack.append(child) return current.value # Usage root = TreeNode("A") root.add_child(TreeNode("B")) root.add_child(TreeNode("C")) root.children[0].add_child(TreeNode("D")) # Clean iteration interface for value in root: print(value) # A, B, D, C # Can also manually control iteration iterator = iter(root) print(next(iterator)) # A print(next(iterator)) # B
import os class Directory: def __init__(self, path): self.path = path def get_all_files(self): """Tight coupling - loads everything into memory at once""" all_files = [] def _collect_files(current_path): try: for item in os.listdir(current_path): item_path = os.path.join(current_path, item) if os.path.isfile(item_path): all_files.append(item_path) elif os.path.isdir(item_path): _collect_files(item_path) # Recursive except PermissionError: pass # Skip inaccessible directories _collect_files(self.path) return all_files # Usage directory = Directory("/some/path") files = directory.get_all_files() # Loads ALL files at once! for file_path in files: print(file_path)
import os class Directory: def __init__(self, path): self.path = path def get_all_files(self): """Tight coupling - loads everything into memory at once""" all_files = [] def _collect_files(current_path): try: for item in os.listdir(current_path): item_path = os.path.join(current_path, item) if os.path.isfile(item_path): all_files.append(item_path) elif os.path.isdir(item_path): _collect_files(item_path) # Recursive except PermissionError: pass # Skip inaccessible directories _collect_files(self.path) return all_files # Usage directory = Directory("/some/path") files = directory.get_all_files() # Loads ALL files at once! for file_path in files: print(file_path)
import os class Directory: def __init__(self, path): self.path = path def __iter__(self): return DirectoryIterator(self.path) class DirectoryIterator: def __init__(self, root_path): self.stack = [root_path] def __iter__(self): return self def __next__(self): while self.stack: current_path = self.stack.pop() if os.path.isfile(current_path): return current_path elif os.path.isdir(current_path): try: items = os.listdir(current_path) for item in reversed(items): item_path = os.path.join(current_path, item) self.stack.append(item_path) except PermissionError: continue raise StopIteration # Usage directory = Directory("/some/path") # Lazy iteration - files yielded one by one for file_path in directory: print(file_path) # Can break early without processing remaining files if file_path.endswith('.txt'): break # Manual control iterator = iter(directory) first_file = next(iterator) second_file = next(iterator)
import os class Directory: def __init__(self, path): self.path = path def __iter__(self): return DirectoryIterator(self.path) class DirectoryIterator: def __init__(self, root_path): self.stack = [root_path] def __iter__(self): return self def __next__(self): while self.stack: current_path = self.stack.pop() if os.path.isfile(current_path): return current_path elif os.path.isdir(current_path): try: items = os.listdir(current_path) for item in reversed(items): item_path = os.path.join(current_path, item) self.stack.append(item_path) except PermissionError: continue raise StopIteration # Usage directory = Directory("/some/path") # Lazy iteration - files yielded one by one for file_path in directory: print(file_path) # Can break early without processing remaining files if file_path.endswith('.txt'): break # Manual control iterator = iter(directory) first_file = next(iterator) second_file = next(iterator)
import os class Directory: def __init__(self, path): self.path = path def __iter__(self): return DirectoryIterator(self.path) class DirectoryIterator: def __init__(self, root_path): self.stack = [root_path] def __iter__(self): return self def __next__(self): while self.stack: current_path = self.stack.pop() if os.path.isfile(current_path): return current_path elif os.path.isdir(current_path): try: items = os.listdir(current_path) for item in reversed(items): item_path = os.path.join(current_path, item) self.stack.append(item_path) except PermissionError: continue raise StopIteration # Usage directory = Directory("/some/path") # Lazy iteration - files yielded one by one for file_path in directory: print(file_path) # Can break early without processing remaining files if file_path.endswith('.txt'): break # Manual control iterator = iter(directory) first_file = next(iterator) second_file = next(iterator)
yield
def simple_counter(): print("Starting...") yield 1 print("After first yield") yield 2 print("After second yield") yield 3 print("Done!") # Usage counter = simple_counter() # Creates generator, doesn't run yet! print("Generator created") print(next(counter)) # Starting... 1 print(next(counter)) # After first yield 2 print(next(counter)) # After second yield 3 # print(next(counter)) # Done! StopIteration
def simple_counter(): print("Starting...") yield 1 print("After first yield") yield 2 print("After second yield") yield 3 print("Done!") # Usage counter = simple_counter() # Creates generator, doesn't run yet! print("Generator created") print(next(counter)) # Starting... 1 print(next(counter)) # After first yield 2 print(next(counter)) # After second yield 3 # print(next(counter)) # Done! StopIteration
def simple_counter(): print("Starting...") yield 1 print("After first yield") yield 2 print("After second yield") yield 3 print("Done!") # Usage counter = simple_counter() # Creates generator, doesn't run yet! print("Generator created") print(next(counter)) # Starting... 1 print(next(counter)) # After first yield 2 print(next(counter)) # After second yield 3 # print(next(counter)) # Done! StopIteration
def simple_counter(): print("Starting...") yield 1 print("After first yield") yield 2 print("After second yield") yield 3 print("Done!") # Usage counter = simple_counter() # Creates generator, doesn't run yet! print("Generator created") print(next(counter)) # Starting... 1 print(next(counter)) # After first yield 2 print(next(counter)) # After second yield 3 # print(next(counter)) # Done! StopIteration
def simple_counter(): print("Starting...") yield 1 print("After first yield") yield 2 print("After second yield") yield 3 print("Done!") # Usage counter = simple_counter() # Creates generator, doesn't run yet! print("Generator created") print(next(counter)) # Starting... 1 print(next(counter)) # After first yield 2 print(next(counter)) # After second yield 3 # print(next(counter)) # Done! StopIteration
def simple_counter(): print("Starting...") yield 1 print("After first yield") yield 2 print("After second yield") yield 3 print("Done!") # Usage counter = simple_counter() # Creates generator, doesn't run yet! print("Generator created") print(next(counter)) # Starting... 1 print(next(counter)) # After first yield 2 print(next(counter)) # After second yield 3 # print(next(counter)) # Done! StopIteration
def simple_counter(): print("Starting...") yield 1 print("After first yield") yield 2 print("After second yield") yield 3 print("Done!") # Usage counter = simple_counter() # Creates generator, doesn't run yet! print("Generator created") print(next(counter)) # Starting... 1 print(next(counter)) # After first yield 2 print(next(counter)) # After second yield 3 # print(next(counter)) # Done! StopIteration
yield
vs return
# With return - function ends def with_return(): return 1 return 2 # Never reached! return 3 # Never reached! # With yield - function pauses def with_yield(): yield 1 # Pause here, remember state yield 2 # Resume here next time yield 3 # Resume here next time print(list(with_yield())) # [1, 2, 3]
# With return - function ends def with_return(): return 1 return 2 # Never reached! return 3 # Never reached! # With yield - function pauses def with_yield(): yield 1 # Pause here, remember state yield 2 # Resume here next time yield 3 # Resume here next time print(list(with_yield())) # [1, 2, 3]
def count_up_to(max_num): count = 1 while count <= max_num: yield count count += 1 # Usage for num in count_up_to(5): print(num) # 1, 2, 3, 4, 5 # Memory efficient - generates one number at a time! numbers = count_up_to(1000000) # Doesn't create million numbers in memory print(next(numbers)) # 1 print(next(numbers)) # 2
def count_up_to(max_num): count = 1 while count <= max_num: yield count count += 1 # Usage for num in count_up_to(5): print(num) # 1, 2, 3, 4, 5 # Memory efficient - generates one number at a time! numbers = count_up_to(1000000) # Doesn't create million numbers in memory print(next(numbers)) # 1 print(next(numbers)) # 2
def count_up_to(max_num): count = 1 while count <= max_num: yield count count += 1 # Usage for num in count_up_to(5): print(num) # 1, 2, 3, 4, 5 # Memory efficient - generates one number at a time! numbers = count_up_to(1000000) # Doesn't create million numbers in memory print(next(numbers)) # 1 print(next(numbers)) # 2
def count_up_to(max_num): count = 1 while count <= max_num: yield count count += 1 # Usage for num in count_up_to(5): print(num) # 1, 2, 3, 4, 5 # Memory efficient - generates one number at a time! numbers = count_up_to(1000000) # Doesn't create million numbers in memory print(next(numbers)) # 1 print(next(numbers)) # 2
yield
import os def iterate_files(path): """Generator function using yield""" stack = [path] while stack: current_path = stack.pop() if os.path.isfile(current_path): yield current_path # Pause here, return file path elif os.path.isdir(current_path): try: items = os.listdir(current_path) for item in reversed(items): item_path = os.path.join(current_path, item) stack.append(item_path) except PermissionError: continue # Usage - same as Iterator pattern but much simpler! for file_path in iterate_files("/some/path"): print(file_path) if file_path.endswith('.txt'): break # Can still break early!
import os def iterate_files(path): """Generator function using yield""" stack = [path] while stack: current_path = stack.pop() if os.path.isfile(current_path): yield current_path # Pause here, return file path elif os.path.isdir(current_path): try: items = os.listdir(current_path) for item in reversed(items): item_path = os.path.join(current_path, item) stack.append(item_path) except PermissionError: continue # Usage - same as Iterator pattern but much simpler! for file_path in iterate_files("/some/path"): print(file_path) if file_path.endswith('.txt'): break # Can still break early!
import os def iterate_files(path): """Generator function using yield""" stack = [path] while stack: current_path = stack.pop() if os.path.isfile(current_path): yield current_path # Pause here, return file path elif os.path.isdir(current_path): try: items = os.listdir(current_path) for item in reversed(items): item_path = os.path.join(current_path, item) stack.append(item_path) except PermissionError: continue # Usage - same as Iterator pattern but much simpler! for file_path in iterate_files("/some/path"): print(file_path) if file_path.endswith('.txt'): break # Can still break early!
Use the Observer pattern when:
class WeatherStation: def __init__(self): self.temperature = 0 self.display1 = Display1() self.display2 = Display2() def set_temperature(self, temp): self.temperature = temp # Tightly coupled - WeatherStation knows about specific displays self.display1.update(temp) self.display2.update(temp) class Display1: def update(self, temp): print(f"Display 1: Temperature is {temp}°C") class Display2: def update(self, temp): print(f"Display 2: Current temp: {temp}°C") # Usage weather = WeatherStation() weather.set_temperature(25)
class WeatherStation: def __init__(self): self.temperature = 0 self.display1 = Display1() self.display2 = Display2() def set_temperature(self, temp): self.temperature = temp # Tightly coupled - WeatherStation knows about specific displays self.display1.update(temp) self.display2.update(temp) class Display1: def update(self, temp): print(f"Display 1: Temperature is {temp}°C") class Display2: def update(self, temp): print(f"Display 2: Current temp: {temp}°C") # Usage weather = WeatherStation() weather.set_temperature(25)
class WeatherStation: def __init__(self): self.temperature = 0 self.display1 = Display1() self.display2 = Display2() def set_temperature(self, temp): self.temperature = temp # Tightly coupled - WeatherStation knows about specific displays self.display1.update(temp) self.display2.update(temp) class Display1: def update(self, temp): print(f"Display 1: Temperature is {temp}°C") class Display2: def update(self, temp): print(f"Display 2: Current temp: {temp}°C") # Usage weather = WeatherStation() weather.set_temperature(25)
class WeatherStation: def __init__(self): self.temperature = 0 self.display1 = Display1() self.display2 = Display2() def set_temperature(self, temp): self.temperature = temp # Tightly coupled - WeatherStation knows about specific displays self.display1.update(temp) self.display2.update(temp) class Display1: def update(self, temp): print(f"Display 1: Temperature is {temp}°C") class Display2: def update(self, temp): print(f"Display 2: Current temp: {temp}°C") # Usage weather = WeatherStation() weather.set_temperature(25)
class WeatherStation: def __init__(self): self.temperature = 0 self.display1 = Display1() self.display2 = Display2() def set_temperature(self, temp): self.temperature = temp # Tightly coupled - WeatherStation knows about specific displays self.display1.update(temp) self.display2.update(temp) class Display1: def update(self, temp): print(f"Display 1: Temperature is {temp}°C") class Display2: def update(self, temp): print(f"Display 2: Current temp: {temp}°C") # Usage weather = WeatherStation() weather.set_temperature(25)
class WeatherStation: def __init__(self): self.temperature = 0 self.observers = [] def add_observer(self, observer): self.observers.append(observer) def remove_observer(self, observer): self.observers.remove(observer) def notify_observers(self): for observer in self.observers: observer.update(self.temperature) def set_temperature(self, temp): self.temperature = temp self.notify_observers() class Display1: def update(self, temp): print(f"Display 1: Temperature is {temp}°C") class Display2: def update(self, temp): print(f"Display 2: Current temp: {temp}°C") # Usage weather = WeatherStation() weather.add_observer(Display1()) weather.add_observer(Display2()) weather.set_temperature(25)
class WeatherStation: def __init__(self): self.temperature = 0 self.observers = [] def add_observer(self, observer): self.observers.append(observer) def remove_observer(self, observer): self.observers.remove(observer) def notify_observers(self): for observer in self.observers: observer.update(self.temperature) def set_temperature(self, temp): self.temperature = temp self.notify_observers() class Display1: def update(self, temp): print(f"Display 1: Temperature is {temp}°C") class Display2: def update(self, temp): print(f"Display 2: Current temp: {temp}°C") # Usage weather = WeatherStation() weather.add_observer(Display1()) weather.add_observer(Display2()) weather.set_temperature(25)
class WeatherStation: def __init__(self): self.temperature = 0 self.observers = [] def add_observer(self, observer): self.observers.append(observer) def remove_observer(self, observer): self.observers.remove(observer) def notify_observers(self): for observer in self.observers: observer.update(self.temperature) def set_temperature(self, temp): self.temperature = temp self.notify_observers() class Display1: def update(self, temp): print(f"Display 1: Temperature is {temp}°C") class Display2: def update(self, temp): print(f"Display 2: Current temp: {temp}°C") # Usage weather = WeatherStation() weather.add_observer(Display1()) weather.add_observer(Display2()) weather.set_temperature(25)
class WeatherStation: def __init__(self): self.temperature = 0 self.observers = [] def add_observer(self, observer): self.observers.append(observer) def remove_observer(self, observer): self.observers.remove(observer) def notify_observers(self): for observer in self.observers: observer.update(self.temperature) def set_temperature(self, temp): self.temperature = temp self.notify_observers() class Display1: def update(self, temp): print(f"Display 1: Temperature is {temp}°C") class Display2: def update(self, temp): print(f"Display 2: Current temp: {temp}°C") # Usage weather = WeatherStation() weather.add_observer(Display1()) weather.add_observer(Display2()) weather.set_temperature(25)
class WeatherStation: def __init__(self): self.temperature = 0 self.observers = [] def add_observer(self, observer): self.observers.append(observer) def remove_observer(self, observer): self.observers.remove(observer) def notify_observers(self): for observer in self.observers: observer.update(self.temperature) def set_temperature(self, temp): self.temperature = temp self.notify_observers() class Display1: def update(self, temp): print(f"Display 1: Temperature is {temp}°C") class Display2: def update(self, temp): print(f"Display 2: Current temp: {temp}°C") # Usage weather = WeatherStation() weather.add_observer(Display1()) weather.add_observer(Display2()) weather.set_temperature(25)
class WeatherStation: def __init__(self): self.temperature = 0 self.observers = [] def add_observer(self, observer): self.observers.append(observer) def remove_observer(self, observer): self.observers.remove(observer) def notify_observers(self): for observer in self.observers: observer.update(self.temperature) def set_temperature(self, temp): self.temperature = temp self.notify_observers() class Display1: def update(self, temp): print(f"Display 1: Temperature is {temp}°C") class Display2: def update(self, temp): print(f"Display 2: Current temp: {temp}°C") # Usage weather = WeatherStation() weather.add_observer(Display1()) weather.add_observer(Display2()) weather.set_temperature(25)
class WeatherStation: def __init__(self): self.temperature = 0 self.observers = [] def add_observer(self, observer): self.observers.append(observer) def remove_observer(self, observer): self.observers.remove(observer) def notify_observers(self): for observer in self.observers: observer.update(self.temperature) def set_temperature(self, temp): self.temperature = temp self.notify_observers() class Display1: def update(self, temp): print(f"Display 1: Temperature is {temp}°C") class Display2: def update(self, temp): print(f"Display 2: Current temp: {temp}°C") # Usage weather = WeatherStation() weather.add_observer(Display1()) weather.add_observer(Display2()) weather.set_temperature(25)
class WeatherStation: def __init__(self): self.temperature = 0 self.observers = [] def add_observer(self, observer): self.observers.append(observer) def remove_observer(self, observer): self.observers.remove(observer) def notify_observers(self): for observer in self.observers: observer.update(self.temperature) def set_temperature(self, temp): self.temperature = temp self.notify_observers() class Display1: def update(self, temp): print(f"Display 1: Temperature is {temp}°C") class Display2: def update(self, temp): print(f"Display 2: Current temp: {temp}°C") # Usage weather = WeatherStation() weather.add_observer(Display1()) weather.add_observer(Display2()) weather.set_temperature(25)
useState
// When you call useState, React internally does something like this: function useState(initialValue) { let state = initialValue; const observers = []; // List of components watching this state const setState = (newValue) => { state = newValue; // Notify all observers (re-render components) observers.forEach(component => component.reRender()); }; // Register this component as an observer observers.push(currentComponent); return [state, setState]; }
// When you call useState, React internally does something like this: function useState(initialValue) { let state = initialValue; const observers = []; // List of components watching this state const setState = (newValue) => { state = newValue; // Notify all observers (re-render components) observers.forEach(component => component.reRender()); }; // Register this component as an observer observers.push(currentComponent); return [state, setState]; }
// When you call useState, React internally does something like this: function useState(initialValue) { let state = initialValue; const observers = []; // List of components watching this state const setState = (newValue) => { state = newValue; // Notify all observers (re-render components) observers.forEach(component => component.reRender()); }; // Register this component as an observer observers.push(currentComponent); return [state, setState]; }
// When you call useState, React internally does something like this: function useState(initialValue) { let state = initialValue; const observers = []; // List of components watching this state const setState = (newValue) => { state = newValue; // Notify all observers (re-render components) observers.forEach(component => component.reRender()); }; // Register this component as an observer observers.push(currentComponent); return [state, setState]; }
function Counter() { const [count, setCount] = useState(0); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}> Increment </button> </div> ); }
function Counter() { const [count, setCount] = useState(0); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}> Increment </button> </div> ); }
function Counter() { const [count, setCount] = useState(0); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}> Increment </button> </div> ); }
function Counter() { const [count, setCount] = useState(0); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}> Increment </button> </div> ); }
Use the State Machine pattern when:
class TrafficLight: def __init__(self): self.state = "RED" def change(self): # Messy conditional logic if self.state == "RED": self.state = "GREEN" print("Green light - Go!") elif self.state == "GREEN": self.state = "YELLOW" print("Yellow light - Slow down!") elif self.state == "YELLOW": self.state = "RED" print("Red light - Stop!") def pedestrian_request(self): # More complex logic spread across method if self.state == "RED": print("Already red, safe to cross") elif self.state == "GREEN": print("Will change to red soon") self.state = "YELLOW" elif self.state == "YELLOW": print("Wait for red light") # Usage light = TrafficLight() light.change() # Green light - Go! light.change() # Yellow light - Slow down! light.pedestrian_request() # Wait for red light
class TrafficLight: def __init__(self): self.state = "RED" def change(self): # Messy conditional logic if self.state == "RED": self.state = "GREEN" print("Green light - Go!") elif self.state == "GREEN": self.state = "YELLOW" print("Yellow light - Slow down!") elif self.state == "YELLOW": self.state = "RED" print("Red light - Stop!") def pedestrian_request(self): # More complex logic spread across method if self.state == "RED": print("Already red, safe to cross") elif self.state == "GREEN": print("Will change to red soon") self.state = "YELLOW" elif self.state == "YELLOW": print("Wait for red light") # Usage light = TrafficLight() light.change() # Green light - Go! light.change() # Yellow light - Slow down! light.pedestrian_request() # Wait for red light
class TrafficLight: def __init__(self): self.state = "RED" def change(self): # Messy conditional logic if self.state == "RED": self.state = "GREEN" print("Green light - Go!") elif self.state == "GREEN": self.state = "YELLOW" print("Yellow light - Slow down!") elif self.state == "YELLOW": self.state = "RED" print("Red light - Stop!") def pedestrian_request(self): # More complex logic spread across method if self.state == "RED": print("Already red, safe to cross") elif self.state == "GREEN": print("Will change to red soon") self.state = "YELLOW" elif self.state == "YELLOW": print("Wait for red light") # Usage light = TrafficLight() light.change() # Green light - Go! light.change() # Yellow light - Slow down! light.pedestrian_request() # Wait for red light
class TrafficLightState: def change(self, light): pass def pedestrian_request(self, light): pass class RedState(TrafficLightState): def change(self, light): print("Green light - Go!") light.state = GreenState() def pedestrian_request(self, light): print("Already red, safe to cross") class GreenState(TrafficLightState): def change(self, light): print("Yellow light - Slow down!") light.state = YellowState() def pedestrian_request(self, light): print("Will change to red soon") light.state = YellowState() class YellowState(TrafficLightState): def change(self, light): print("Red light - Stop!") light.state = RedState() def pedestrian_request(self, light): print("Wait for red light") class TrafficLight: def __init__(self): self.state = RedState() def change(self): self.state.change(self) def pedestrian_request(self): self.state.pedestrian_request(self) # Usage - same interface light = TrafficLight() light.change() # Green light - Go! light.change() # Yellow light - Slow down! light.pedestrian_request() # Wait for red light
class TrafficLightState: def change(self, light): pass def pedestrian_request(self, light): pass class RedState(TrafficLightState): def change(self, light): print("Green light - Go!") light.state = GreenState() def pedestrian_request(self, light): print("Already red, safe to cross") class GreenState(TrafficLightState): def change(self, light): print("Yellow light - Slow down!") light.state = YellowState() def pedestrian_request(self, light): print("Will change to red soon") light.state = YellowState() class YellowState(TrafficLightState): def change(self, light): print("Red light - Stop!") light.state = RedState() def pedestrian_request(self, light): print("Wait for red light") class TrafficLight: def __init__(self): self.state = RedState() def change(self): self.state.change(self) def pedestrian_request(self): self.state.pedestrian_request(self) # Usage - same interface light = TrafficLight() light.change() # Green light - Go! light.change() # Yellow light - Slow down! light.pedestrian_request() # Wait for red light
class TrafficLightState: def change(self, light): pass def pedestrian_request(self, light): pass class RedState(TrafficLightState): def change(self, light): print("Green light - Go!") light.state = GreenState() def pedestrian_request(self, light): print("Already red, safe to cross") class GreenState(TrafficLightState): def change(self, light): print("Yellow light - Slow down!") light.state = YellowState() def pedestrian_request(self, light): print("Will change to red soon") light.state = YellowState() class YellowState(TrafficLightState): def change(self, light): print("Red light - Stop!") light.state = RedState() def pedestrian_request(self, light): print("Wait for red light") class TrafficLight: def __init__(self): self.state = RedState() def change(self): self.state.change(self) def pedestrian_request(self): self.state.pedestrian_request(self) # Usage - same interface light = TrafficLight() light.change() # Green light - Go! light.change() # Yellow light - Slow down! light.pedestrian_request() # Wait for red light
class TrafficLightState: def change(self, light): pass def pedestrian_request(self, light): pass class RedState(TrafficLightState): def change(self, light): print("Green light - Go!") light.state = GreenState() def pedestrian_request(self, light): print("Already red, safe to cross") class GreenState(TrafficLightState): def change(self, light): print("Yellow light - Slow down!") light.state = YellowState() def pedestrian_request(self, light): print("Will change to red soon") light.state = YellowState() class YellowState(TrafficLightState): def change(self, light): print("Red light - Stop!") light.state = RedState() def pedestrian_request(self, light): print("Wait for red light") class TrafficLight: def __init__(self): self.state = RedState() def change(self): self.state.change(self) def pedestrian_request(self): self.state.pedestrian_request(self) # Usage - same interface light = TrafficLight() light.change() # Green light - Go! light.change() # Yellow light - Slow down! light.pedestrian_request() # Wait for red light
class TrafficLightState: def change(self, light): pass def pedestrian_request(self, light): pass class RedState(TrafficLightState): def change(self, light): print("Green light - Go!") light.state = GreenState() def pedestrian_request(self, light): print("Already red, safe to cross") class GreenState(TrafficLightState): def change(self, light): print("Yellow light - Slow down!") light.state = YellowState() def pedestrian_request(self, light): print("Will change to red soon") light.state = YellowState() class YellowState(TrafficLightState): def change(self, light): print("Red light - Stop!") light.state = RedState() def pedestrian_request(self, light): print("Wait for red light") class TrafficLight: def __init__(self): self.state = RedState() def change(self): self.state.change(self) def pedestrian_request(self): self.state.pedestrian_request(self) # Usage - same interface light = TrafficLight() light.change() # Green light - Go! light.change() # Yellow light - Slow down! light.pedestrian_request() # Wait for red light
class TrafficLightState: def change(self, light): pass def pedestrian_request(self, light): pass class RedState(TrafficLightState): def change(self, light): print("Green light - Go!") light.state = GreenState() def pedestrian_request(self, light): print("Already red, safe to cross") class GreenState(TrafficLightState): def change(self, light): print("Yellow light - Slow down!") light.state = YellowState() def pedestrian_request(self, light): print("Will change to red soon") light.state = YellowState() class YellowState(TrafficLightState): def change(self, light): print("Red light - Stop!") light.state = RedState() def pedestrian_request(self, light): print("Wait for red light") class TrafficLight: def __init__(self): self.state = RedState() def change(self): self.state.change(self) def pedestrian_request(self): self.state.pedestrian_request(self) # Usage - same interface light = TrafficLight() light.change() # Green light - Go! light.change() # Yellow light - Slow down! light.pedestrian_request() # Wait for red light
class TrafficLightState: def change(self, light): pass def pedestrian_request(self, light): pass class RedState(TrafficLightState): def change(self, light): print("Green light - Go!") light.state = GreenState() def pedestrian_request(self, light): print("Already red, safe to cross") class GreenState(TrafficLightState): def change(self, light): print("Yellow light - Slow down!") light.state = YellowState() def pedestrian_request(self, light): print("Will change to red soon") light.state = YellowState() class YellowState(TrafficLightState): def change(self, light): print("Red light - Stop!") light.state = RedState() def pedestrian_request(self, light): print("Wait for red light") class TrafficLight: def __init__(self): self.state = RedState() def change(self): self.state.change(self) def pedestrian_request(self): self.state.pedestrian_request(self) # Usage - same interface light = TrafficLight() light.change() # Green light - Go! light.change() # Yellow light - Slow down! light.pedestrian_request() # Wait for red light
class TrafficLightState: def change(self, light): pass def pedestrian_request(self, light): pass class RedState(TrafficLightState): def change(self, light): print("Green light - Go!") light.state = GreenState() def pedestrian_request(self, light): print("Already red, safe to cross") class GreenState(TrafficLightState): def change(self, light): print("Yellow light - Slow down!") light.state = YellowState() def pedestrian_request(self, light): print("Will change to red soon") light.state = YellowState() class YellowState(TrafficLightState): def change(self, light): print("Red light - Stop!") light.state = RedState() def pedestrian_request(self, light): print("Wait for red light") class TrafficLight: def __init__(self): self.state = RedState() def change(self): self.state.change(self) def pedestrian_request(self): self.state.pedestrian_request(self) # Usage - same interface light = TrafficLight() light.change() # Green light - Go! light.change() # Yellow light - Slow down! light.pedestrian_request() # Wait for red light
class TrafficLightState: def change(self, light): pass def pedestrian_request(self, light): pass class RedState(TrafficLightState): def change(self, light): print("Green light - Go!") light.state = GreenState() def pedestrian_request(self, light): print("Already red, safe to cross") class GreenState(TrafficLightState): def change(self, light): print("Yellow light - Slow down!") light.state = YellowState() def pedestrian_request(self, light): print("Will change to red soon") light.state = YellowState() class YellowState(TrafficLightState): def change(self, light): print("Red light - Stop!") light.state = RedState() def pedestrian_request(self, light): print("Wait for red light") class TrafficLight: def __init__(self): self.state = RedState() def change(self): self.state.change(self) def pedestrian_request(self): self.state.pedestrian_request(self) # Usage - same interface light = TrafficLight() light.change() # Green light - Go! light.change() # Yellow light - Slow down! light.pedestrian_request() # Wait for red light
class TrafficLightState: def change(self, light): pass def pedestrian_request(self, light): pass class RedState(TrafficLightState): def change(self, light): print("Green light - Go!") light.state = GreenState() def pedestrian_request(self, light): print("Already red, safe to cross") class GreenState(TrafficLightState): def change(self, light): print("Yellow light - Slow down!") light.state = YellowState() def pedestrian_request(self, light): print("Will change to red soon") light.state = YellowState() class YellowState(TrafficLightState): def change(self, light): print("Red light - Stop!") light.state = RedState() def pedestrian_request(self, light): print("Wait for red light") class TrafficLight: def __init__(self): self.state = RedState() def change(self): self.state.change(self) def pedestrian_request(self): self.state.pedestrian_request(self) # Usage - same interface light = TrafficLight() light.change() # Green light - Go! light.change() # Yellow light - Slow down! light.pedestrian_request() # Wait for red light
class TrafficLightState: def change(self, light): pass def pedestrian_request(self, light): pass class RedState(TrafficLightState): def change(self, light): print("Green light - Go!") light.state = GreenState() def pedestrian_request(self, light): print("Already red, safe to cross") class GreenState(TrafficLightState): def change(self, light): print("Yellow light - Slow down!") light.state = YellowState() def pedestrian_request(self, light): print("Will change to red soon") light.state = YellowState() class YellowState(TrafficLightState): def change(self, light): print("Red light - Stop!") light.state = RedState() def pedestrian_request(self, light): print("Wait for red light") class TrafficLight: def __init__(self): self.state = RedState() def change(self): self.state.change(self) def pedestrian_request(self): self.state.pedestrian_request(self) # Usage - same interface light = TrafficLight() light.change() # Green light - Go! light.change() # Yellow light - Slow down! light.pedestrian_request() # Wait for red light
class TrafficLightState: def change(self, light): pass def pedestrian_request(self, light): pass class RedState(TrafficLightState): def change(self, light): print("Green light - Go!") light.state = GreenState() def pedestrian_request(self, light): print("Already red, safe to cross") class GreenState(TrafficLightState): def change(self, light): print("Yellow light - Slow down!") light.state = YellowState() def pedestrian_request(self, light): print("Will change to red soon") light.state = YellowState() class YellowState(TrafficLightState): def change(self, light): print("Red light - Stop!") light.state = RedState() def pedestrian_request(self, light): print("Wait for red light") class TrafficLight: def __init__(self): self.state = RedState() def change(self): self.state.change(self) def pedestrian_request(self): self.state.pedestrian_request(self) # Usage - same interface light = TrafficLight() light.change() # Green light - Go! light.change() # Yellow light - Slow down! light.pedestrian_request() # Wait for red light
https://github.com/state-machines/state_machines
class Vehicle
attr_accessor :seatbelt_on, :time_used, :auto_shop_busy, :parking_meter_number
state_machine :state, initial: :parked do
before_transition parked: any - :parked, do: :put_on_seatbelt
after_transition on: :crash, do: :tow
after_transition on: :repair, do: :fix
after_transition any => :parked do |vehicle, transition|
vehicle.seatbelt_on = false
end
after_failure on: :ignite, do: :log_start_failure
around_transition do |vehicle, transition, block|
start = Time.now
block.call
vehicle.time_used += Time.now - start
end
event :park do
transition [:idling, :first_gear] => :parked
end
before_transition on: :park do |vehicle, transition|
# If using Rails:
# options = transition.args.extract_options!
options = transition.args.last.is_a?(Hash) ? transition.args.pop : {}
meter_number = options[:meter_number]
unless meter_number.nil?
vehicle.parking_meter_number = meter_number
end
end
event :ignite do
transition stalled: same, parked: :idling
end
event :idle do
transition first_gear: :idling
end
event :shift_up do
transition idling: :first_gear, first_gear: :second_gear, second_gear: :third_gear
end
event :shift_down do
transition third_gear: :second_gear, second_gear: :first_gear
end
event :crash do
transition all - [:parked, :stalled] => :stalled, if: ->(vehicle) {!vehicle.passed_inspection?}
end
event :repair do
# The first transition that matches the state and passes its conditions
# will be used
transition stalled: :parked, unless: :auto_shop_busy
transition stalled: same
end
state :parked do
def speed
0
end
end
state :idling, :first_gear do
def speed
10
end
end
state all - [:parked, :stalled, :idling] do
def moving?
true
end
end
state :parked, :stalled, :idling do
def moving?
false
end
end
end
state_machine :alarm_state, initial: :active, namespace: :'alarm' do
event :enable do
transition all => :active
end
event :disable do
transition all => :off
end
state :active, :value => 1
state :off, :value => 0
end
def initialize
@seatbelt_on = false
@time_used = 0
@auto_shop_busy = true
@parking_meter_number = nil
super() # NOTE: This *must* be called, otherwise states won't get initialized
end
def put_on_seatbelt
@seatbelt_on = true
end
def passed_inspection?
false
end
def tow
# tow the vehicle
end
def fix
# get the vehicle fixed by a mechanic
end
def log_start_failure
# log a failed attempt to start the vehicle
end
end
Use the Factory pattern when:
class PostgresConnection: def connect(self): return "Connected to PostgreSQL" class MySQLConnection: def connect(self): return "Connected to MySQL" def create_database(db_type): # Tight coupling - client knows all implementations if db_type == "postgres": return PostgresConnection() elif db_type == "mysql": return MySQLConnection() else: raise ValueError("Unknown database type") # Usage db = create_database("postgres") print(db.connect()) # Problem: Adding new DB requires modifying this function # Also, what if we need different configs for test/prod?
class PostgresConnection: def connect(self): return "Connected to PostgreSQL" class MySQLConnection: def connect(self): return "Connected to MySQL" def create_database(db_type): # Tight coupling - client knows all implementations if db_type == "postgres": return PostgresConnection() elif db_type == "mysql": return MySQLConnection() else: raise ValueError("Unknown database type") # Usage db = create_database("postgres") print(db.connect()) # Problem: Adding new DB requires modifying this function # Also, what if we need different configs for test/prod?
class PostgresConnection: def connect(self): return "Connected to PostgreSQL" class MySQLConnection: def connect(self): return "Connected to MySQL" def create_database(db_type): # Tight coupling - client knows all implementations if db_type == "postgres": return PostgresConnection() elif db_type == "mysql": return MySQLConnection() else: raise ValueError("Unknown database type") # Usage db = create_database("postgres") print(db.connect()) # Problem: Adding new DB requires modifying this function # Also, what if we need different configs for test/prod?
from abc import ABC, abstractmethod class Database(ABC): @abstractmethod def connect(self): pass class PostgresConnection(Database): def connect(self): return "Connected to PostgreSQL" class MySQLConnection(Database): def connect(self): return "Connected to MySQL" class DatabaseFactory: @staticmethod def create_database(db_type: str) -> Database: factories = { "postgres": PostgresConnection, "mysql": MySQLConnection, } db_class = factories.get(db_type) if not db_class: raise ValueError(f"Unknown database type: {db_type}") return db_class() # Can also have specialized factories class TestDatabaseFactory: @staticmethod def create_database(db_type: str) -> Database: # Return test-specific implementations pass # Usage db = DatabaseFactory.create_database("postgres") print(db.connect())
from abc import ABC, abstractmethod class Database(ABC): @abstractmethod def connect(self): pass class PostgresConnection(Database): def connect(self): return "Connected to PostgreSQL" class MySQLConnection(Database): def connect(self): return "Connected to MySQL" class DatabaseFactory: @staticmethod def create_database(db_type: str) -> Database: factories = { "postgres": PostgresConnection, "mysql": MySQLConnection, } db_class = factories.get(db_type) if not db_class: raise ValueError(f"Unknown database type: {db_type}") return db_class() # Can also have specialized factories class TestDatabaseFactory: @staticmethod def create_database(db_type: str) -> Database: # Return test-specific implementations pass # Usage db = DatabaseFactory.create_database("postgres") print(db.connect())
from abc import ABC, abstractmethod class Database(ABC): @abstractmethod def connect(self): pass class PostgresConnection(Database): def connect(self): return "Connected to PostgreSQL" class MySQLConnection(Database): def connect(self): return "Connected to MySQL" class DatabaseFactory: @staticmethod def create_database(db_type: str) -> Database: factories = { "postgres": PostgresConnection, "mysql": MySQLConnection, } db_class = factories.get(db_type) if not db_class: raise ValueError(f"Unknown database type: {db_type}") return db_class() # Can also have specialized factories class TestDatabaseFactory: @staticmethod def create_database(db_type: str) -> Database: # Return test-specific implementations pass # Usage db = DatabaseFactory.create_database("postgres") print(db.connect())
from abc import ABC, abstractmethod class Database(ABC): @abstractmethod def connect(self): pass class PostgresConnection(Database): def connect(self): return "Connected to PostgreSQL" class MySQLConnection(Database): def connect(self): return "Connected to MySQL" class DatabaseFactory: @staticmethod def create_database(db_type: str) -> Database: factories = { "postgres": PostgresConnection, "mysql": MySQLConnection, } db_class = factories.get(db_type) if not db_class: raise ValueError(f"Unknown database type: {db_type}") return db_class() # Can also have specialized factories class TestDatabaseFactory: @staticmethod def create_database(db_type: str) -> Database: # Return test-specific implementations pass # Usage db = DatabaseFactory.create_database("postgres") print(db.connect())
Use the Builder pattern when:
# Without Builder - Complex constructor query = SQLQuery( select=["id", "name", "email"], from_table="users", joins=[{"type": "LEFT", "table": "orders", "on": "users.id = orders.user_id"}], where=["age > 18", "status = 'active'"], order_by="created_at DESC", limit=10 ) # Or string concatenation - error prone query = "SELECT id, name, email " query += "FROM users " query += "LEFT JOIN orders ON users.id = orders.user_id " query += "WHERE age > 18 AND status = 'active' " query += "ORDER BY created_at DESC LIMIT 10"
# Without Builder - Complex constructor query = SQLQuery( select=["id", "name", "email"], from_table="users", joins=[{"type": "LEFT", "table": "orders", "on": "users.id = orders.user_id"}], where=["age > 18", "status = 'active'"], order_by="created_at DESC", limit=10 ) # Or string concatenation - error prone query = "SELECT id, name, email " query += "FROM users " query += "LEFT JOIN orders ON users.id = orders.user_id " query += "WHERE age > 18 AND status = 'active' " query += "ORDER BY created_at DESC LIMIT 10"
# Without Builder - Complex constructor query = SQLQuery( select=["id", "name", "email"], from_table="users", joins=[{"type": "LEFT", "table": "orders", "on": "users.id = orders.user_id"}], where=["age > 18", "status = 'active'"], order_by="created_at DESC", limit=10 ) # Or string concatenation - error prone query = "SELECT id, name, email " query += "FROM users " query += "LEFT JOIN orders ON users.id = orders.user_id " query += "WHERE age > 18 AND status = 'active' " query += "ORDER BY created_at DESC LIMIT 10"
class SQLQueryBuilder: def __init__(self): self.query_parts = { "select": [], "from": "", "joins": [], "where": [], "order_by": "", "limit": None } def select(self, *columns): self.query_parts["select"].extend(columns) return self def from_table(self, table): self.query_parts["from"] = table return self def left_join(self, table, on): self.query_parts["joins"].append(f"LEFT JOIN {table} ON {on}") return self def where(self, condition): self.query_parts["where"].append(condition) return self def order_by(self, column, direction="ASC"): self.query_parts["order_by"] = f"{column} {direction}" return self def limit(self, count): self.query_parts["limit"] = count return self def build(self): # Build the actual query string parts = [] if self.query_parts["select"]: parts.append(f"SELECT {', '.join(self.query_parts['select'])}") if self.query_parts["from"]: parts.append(f"FROM {self.query_parts['from']}") for join in self.query_parts["joins"]: parts.append(join) if self.query_parts["where"]: parts.append(f"WHERE {' AND '.join(self.query_parts['where'])}") if self.query_parts["order_by"]: parts.append(f"ORDER BY {self.query_parts['order_by']}") if self.query_parts["limit"]: parts.append(f"LIMIT {self.query_parts['limit']}") return " ".join(parts) # Usage - Fluent interface query = (SQLQueryBuilder() .select("id", "name", "email") .from_table("users") .left_join("orders", "users.id = orders.user_id") .where("age > 18") .where("status = 'active'") .order_by("created_at", "DESC") .limit(10) .build() ) print(query)
class SQLQueryBuilder: def __init__(self): self.query_parts = { "select": [], "from": "", "joins": [], "where": [], "order_by": "", "limit": None } def select(self, *columns): self.query_parts["select"].extend(columns) return self def from_table(self, table): self.query_parts["from"] = table return self def left_join(self, table, on): self.query_parts["joins"].append(f"LEFT JOIN {table} ON {on}") return self def where(self, condition): self.query_parts["where"].append(condition) return self def order_by(self, column, direction="ASC"): self.query_parts["order_by"] = f"{column} {direction}" return self def limit(self, count): self.query_parts["limit"] = count return self def build(self): # Build the actual query string parts = [] if self.query_parts["select"]: parts.append(f"SELECT {', '.join(self.query_parts['select'])}") if self.query_parts["from"]: parts.append(f"FROM {self.query_parts['from']}") for join in self.query_parts["joins"]: parts.append(join) if self.query_parts["where"]: parts.append(f"WHERE {' AND '.join(self.query_parts['where'])}") if self.query_parts["order_by"]: parts.append(f"ORDER BY {self.query_parts['order_by']}") if self.query_parts["limit"]: parts.append(f"LIMIT {self.query_parts['limit']}") return " ".join(parts) # Usage - Fluent interface query = (SQLQueryBuilder() .select("id", "name", "email") .from_table("users") .left_join("orders", "users.id = orders.user_id") .where("age > 18") .where("status = 'active'") .order_by("created_at", "DESC") .limit(10) .build() ) print(query)
class SQLQueryBuilder: def __init__(self): self.query_parts = { "select": [], "from": "", "joins": [], "where": [], "order_by": "", "limit": None } def select(self, *columns): self.query_parts["select"].extend(columns) return self def from_table(self, table): self.query_parts["from"] = table return self def left_join(self, table, on): self.query_parts["joins"].append(f"LEFT JOIN {table} ON {on}") return self def where(self, condition): self.query_parts["where"].append(condition) return self def order_by(self, column, direction="ASC"): self.query_parts["order_by"] = f"{column} {direction}" return self def limit(self, count): self.query_parts["limit"] = count return self def build(self): # Build the actual query string parts = [] if self.query_parts["select"]: parts.append(f"SELECT {', '.join(self.query_parts['select'])}") if self.query_parts["from"]: parts.append(f"FROM {self.query_parts['from']}") for join in self.query_parts["joins"]: parts.append(join) if self.query_parts["where"]: parts.append(f"WHERE {' AND '.join(self.query_parts['where'])}") if self.query_parts["order_by"]: parts.append(f"ORDER BY {self.query_parts['order_by']}") if self.query_parts["limit"]: parts.append(f"LIMIT {self.query_parts['limit']}") return " ".join(parts) # Usage - Fluent interface query = (SQLQueryBuilder() .select("id", "name", "email") .from_table("users") .left_join("orders", "users.id = orders.user_id") .where("age > 18") .where("status = 'active'") .order_by("created_at", "DESC") .limit(10) .build() ) print(query)
class SQLQueryBuilder: def __init__(self): self.query_parts = { "select": [], "from": "", "joins": [], "where": [], "order_by": "", "limit": None } def select(self, *columns): self.query_parts["select"].extend(columns) return self def from_table(self, table): self.query_parts["from"] = table return self def left_join(self, table, on): self.query_parts["joins"].append(f"LEFT JOIN {table} ON {on}") return self def where(self, condition): self.query_parts["where"].append(condition) return self def order_by(self, column, direction="ASC"): self.query_parts["order_by"] = f"{column} {direction}" return self def limit(self, count): self.query_parts["limit"] = count return self def build(self): # Build the actual query string parts = [] if self.query_parts["select"]: parts.append(f"SELECT {', '.join(self.query_parts['select'])}") if self.query_parts["from"]: parts.append(f"FROM {self.query_parts['from']}") for join in self.query_parts["joins"]: parts.append(join) if self.query_parts["where"]: parts.append(f"WHERE {' AND '.join(self.query_parts['where'])}") if self.query_parts["order_by"]: parts.append(f"ORDER BY {self.query_parts['order_by']}") if self.query_parts["limit"]: parts.append(f"LIMIT {self.query_parts['limit']}") return " ".join(parts) # Usage - Fluent interface query = (SQLQueryBuilder() .select("id", "name", "email") .from_table("users") .left_join("orders", "users.id = orders.user_id") .where("age > 18") .where("status = 'active'") .order_by("created_at", "DESC") .limit(10) .build() ) print(query)
class UIBuilder(private val context: Context) { private val components = mutableListOf<View>() private var layoutParams: ViewGroup.LayoutParams? = null private var backgroundColor: Int? = null private var padding: Int = 0 fun addText(text: String, size: Float = 16f): UIBuilder { val textView = TextView(context).apply { this.text = text textSize = size } components.add(textView) return this } fun addButton(text: String, onClick: () -> Unit): UIBuilder { val button = Button(context).apply { this.text = text setOnClickListener { onClick() } } components.add(button) return this } fun setBackground(color: Int): UIBuilder { backgroundColor = color return this } fun setPadding(padding: Int): UIBuilder { this.padding = padding return this } fun build(): LinearLayout { return LinearLayout(context).apply { orientation = LinearLayout.VERTICAL backgroundColor?.let { setBackgroundColor(it) } if (padding > 0) setPadding(padding, padding, padding, padding) components.forEach { component -> addView(component) } } } } // Usage - Clean and readable val layout = UIBuilder(context) .addText("Welcome to the App", 24f) .addText("Please choose an option:") .addButton("Start") { startProcess() } .addButton("Settings") { openSettings() } .setBackground(Color.WHITE) .setPadding(16) .build()
class UIBuilder(private val context: Context) { private val components = mutableListOf<View>() private var layoutParams: ViewGroup.LayoutParams? = null private var backgroundColor: Int? = null private var padding: Int = 0 fun addText(text: String, size: Float = 16f): UIBuilder { val textView = TextView(context).apply { this.text = text textSize = size } components.add(textView) return this } fun addButton(text: String, onClick: () -> Unit): UIBuilder { val button = Button(context).apply { this.text = text setOnClickListener { onClick() } } components.add(button) return this } fun setBackground(color: Int): UIBuilder { backgroundColor = color return this } fun setPadding(padding: Int): UIBuilder { this.padding = padding return this } fun build(): LinearLayout { return LinearLayout(context).apply { orientation = LinearLayout.VERTICAL backgroundColor?.let { setBackgroundColor(it) } if (padding > 0) setPadding(padding, padding, padding, padding) components.forEach { component -> addView(component) } } } } // Usage - Clean and readable val layout = UIBuilder(context) .addText("Welcome to the App", 24f) .addText("Please choose an option:") .addButton("Start") { startProcess() } .addButton("Settings") { openSettings() } .setBackground(Color.WHITE) .setPadding(16) .build()
class UIBuilder(private val context: Context) { private val components = mutableListOf<View>() private var layoutParams: ViewGroup.LayoutParams? = null private var backgroundColor: Int? = null private var padding: Int = 0 fun addText(text: String, size: Float = 16f): UIBuilder { val textView = TextView(context).apply { this.text = text textSize = size } components.add(textView) return this } fun addButton(text: String, onClick: () -> Unit): UIBuilder { val button = Button(context).apply { this.text = text setOnClickListener { onClick() } } components.add(button) return this } fun setBackground(color: Int): UIBuilder { backgroundColor = color return this } fun setPadding(padding: Int): UIBuilder { this.padding = padding return this } fun build(): LinearLayout { return LinearLayout(context).apply { orientation = LinearLayout.VERTICAL backgroundColor?.let { setBackgroundColor(it) } if (padding > 0) setPadding(padding, padding, padding, padding) components.forEach { component -> addView(component) } } } } // Usage - Clean and readable val layout = UIBuilder(context) .addText("Welcome to the App", 24f) .addText("Please choose an option:") .addButton("Start") { startProcess() } .addButton("Settings") { openSettings() } .setBackground(Color.WHITE) .setPadding(16) .build()
Use the Singleton pattern when:
class ConfigManager: def __init__(self): print("Loading configuration...") self.config = self._load_config() def _load_config(self): # Expensive operation return {"api_key": "secret", "debug": True} def get(self, key): return self.config.get(key) # Problem: Multiple instances config1 = ConfigManager() # Loading configuration... config2 = ConfigManager() # Loading configuration... (again!) # Wastes resources, may cause inconsistencies print(config1 is config2) # False print(id(config1), id(config2)) # Different objects
class ConfigManager: def __init__(self): print("Loading configuration...") self.config = self._load_config() def _load_config(self): # Expensive operation return {"api_key": "secret", "debug": True} def get(self, key): return self.config.get(key) # Problem: Multiple instances config1 = ConfigManager() # Loading configuration... config2 = ConfigManager() # Loading configuration... (again!) # Wastes resources, may cause inconsistencies print(config1 is config2) # False print(id(config1), id(config2)) # Different objects
class ConfigManager: def __init__(self): print("Loading configuration...") self.config = self._load_config() def _load_config(self): # Expensive operation return {"api_key": "secret", "debug": True} def get(self, key): return self.config.get(key) # Problem: Multiple instances config1 = ConfigManager() # Loading configuration... config2 = ConfigManager() # Loading configuration... (again!) # Wastes resources, may cause inconsistencies print(config1 is config2) # False print(id(config1), id(config2)) # Different objects
class ConfigManager: _instance = None _initialized = False def __new__(cls): if cls._instance is None: cls._instance = super().__new__(cls) return cls._instance def __init__(self): # Ensure initialization happens only once if not ConfigManager._initialized: print("Loading configuration...") self.config = self._load_config() ConfigManager._initialized = True def _load_config(self): # Expensive operation return {"api_key": "secret", "debug": True} def get(self, key): return self.config.get(key) # Usage - Same instance always config1 = ConfigManager() # Loading configuration... config2 = ConfigManager() # No loading, returns same instance print(config1 is config2) # True print(id(config1), id(config2)) # Same object ID # Modern Python alternative: modules are singletons # config.py # _config = load_config() # def get(key): return _config.get(key)
class ConfigManager: _instance = None _initialized = False def __new__(cls): if cls._instance is None: cls._instance = super().__new__(cls) return cls._instance def __init__(self): # Ensure initialization happens only once if not ConfigManager._initialized: print("Loading configuration...") self.config = self._load_config() ConfigManager._initialized = True def _load_config(self): # Expensive operation return {"api_key": "secret", "debug": True} def get(self, key): return self.config.get(key) # Usage - Same instance always config1 = ConfigManager() # Loading configuration... config2 = ConfigManager() # No loading, returns same instance print(config1 is config2) # True print(id(config1), id(config2)) # Same object ID # Modern Python alternative: modules are singletons # config.py # _config = load_config() # def get(key): return _config.get(key)
class ConfigManager: _instance = None _initialized = False def __new__(cls): if cls._instance is None: cls._instance = super().__new__(cls) return cls._instance def __init__(self): # Ensure initialization happens only once if not ConfigManager._initialized: print("Loading configuration...") self.config = self._load_config() ConfigManager._initialized = True def _load_config(self): # Expensive operation return {"api_key": "secret", "debug": True} def get(self, key): return self.config.get(key) # Usage - Same instance always config1 = ConfigManager() # Loading configuration... config2 = ConfigManager() # No loading, returns same instance print(config1 is config2) # True print(id(config1), id(config2)) # Same object ID # Modern Python alternative: modules are singletons # config.py # _config = load_config() # def get(key): return _config.get(key)
class ConfigManager: _instance = None _initialized = False def __new__(cls): if cls._instance is None: cls._instance = super().__new__(cls) return cls._instance def __init__(self): # Ensure initialization happens only once if not ConfigManager._initialized: print("Loading configuration...") self.config = self._load_config() ConfigManager._initialized = True def _load_config(self): # Expensive operation return {"api_key": "secret", "debug": True} def get(self, key): return self.config.get(key) # Usage - Same instance always config1 = ConfigManager() # Loading configuration... config2 = ConfigManager() # No loading, returns same instance print(config1 is config2) # True print(id(config1), id(config2)) # Same object ID # Modern Python alternative: modules are singletons # config.py # _config = load_config() # def get(key): return _config.get(key)
class ConfigManager: _instance = None _initialized = False def __new__(cls): if cls._instance is None: cls._instance = super().__new__(cls) return cls._instance def __init__(self): # Ensure initialization happens only once if not ConfigManager._initialized: print("Loading configuration...") self.config = self._load_config() ConfigManager._initialized = True def _load_config(self): # Expensive operation return {"api_key": "secret", "debug": True} def get(self, key): return self.config.get(key) # Usage - Same instance always config1 = ConfigManager() # Loading configuration... config2 = ConfigManager() # No loading, returns same instance print(config1 is config2) # True print(id(config1), id(config2)) # Same object ID # Modern Python alternative: modules are singletons # config.py # _config = load_config() # def get(key): return _config.get(key)
Use the Decorator pattern when:
# Without Decorator - Class explosion class Coffee: def cost(self): return 5 class CoffeeWithMilk: def cost(self): return 5 + 2 # base + milk class CoffeeWithMilkAndSugar: def cost(self): return 5 + 2 + 1 # base + milk + sugar class CoffeeWithMilkAndSugarAndWhip: def cost(self): return 5 + 2 + 1 + 3 # base + milk + sugar + whip # Problem: Need a new class for every combination! # What about CoffeeWithSugar? CoffeeWithWhip? # What about double milk? Triple sugar? coffee = CoffeeWithMilkAndSugar() print(f"Cost: ${coffee.cost()}")
# Without Decorator - Class explosion class Coffee: def cost(self): return 5 class CoffeeWithMilk: def cost(self): return 5 + 2 # base + milk class CoffeeWithMilkAndSugar: def cost(self): return 5 + 2 + 1 # base + milk + sugar class CoffeeWithMilkAndSugarAndWhip: def cost(self): return 5 + 2 + 1 + 3 # base + milk + sugar + whip # Problem: Need a new class for every combination! # What about CoffeeWithSugar? CoffeeWithWhip? # What about double milk? Triple sugar? coffee = CoffeeWithMilkAndSugar() print(f"Cost: ${coffee.cost()}")
# Without Decorator - Class explosion class Coffee: def cost(self): return 5 class CoffeeWithMilk: def cost(self): return 5 + 2 # base + milk class CoffeeWithMilkAndSugar: def cost(self): return 5 + 2 + 1 # base + milk + sugar class CoffeeWithMilkAndSugarAndWhip: def cost(self): return 5 + 2 + 1 + 3 # base + milk + sugar + whip # Problem: Need a new class for every combination! # What about CoffeeWithSugar? CoffeeWithWhip? # What about double milk? Triple sugar? coffee = CoffeeWithMilkAndSugar() print(f"Cost: ${coffee.cost()}")
# Without Decorator - Class explosion class Coffee: def cost(self): return 5 class CoffeeWithMilk: def cost(self): return 5 + 2 # base + milk class CoffeeWithMilkAndSugar: def cost(self): return 5 + 2 + 1 # base + milk + sugar class CoffeeWithMilkAndSugarAndWhip: def cost(self): return 5 + 2 + 1 + 3 # base + milk + sugar + whip # Problem: Need a new class for every combination! # What about CoffeeWithSugar? CoffeeWithWhip? # What about double milk? Triple sugar? coffee = CoffeeWithMilkAndSugar() print(f"Cost: ${coffee.cost()}")
# Without Decorator - Class explosion class Coffee: def cost(self): return 5 class CoffeeWithMilk: def cost(self): return 5 + 2 # base + milk class CoffeeWithMilkAndSugar: def cost(self): return 5 + 2 + 1 # base + milk + sugar class CoffeeWithMilkAndSugarAndWhip: def cost(self): return 5 + 2 + 1 + 3 # base + milk + sugar + whip # Problem: Need a new class for every combination! # What about CoffeeWithSugar? CoffeeWithWhip? # What about double milk? Triple sugar? coffee = CoffeeWithMilkAndSugar() print(f"Cost: ${coffee.cost()}")
# Without Decorator - Class explosion class Coffee: def cost(self): return 5 class CoffeeWithMilk: def cost(self): return 5 + 2 # base + milk class CoffeeWithMilkAndSugar: def cost(self): return 5 + 2 + 1 # base + milk + sugar class CoffeeWithMilkAndSugarAndWhip: def cost(self): return 5 + 2 + 1 + 3 # base + milk + sugar + whip # Problem: Need a new class for every combination! # What about CoffeeWithSugar? CoffeeWithWhip? # What about double milk? Triple sugar? coffee = CoffeeWithMilkAndSugar() print(f"Cost: ${coffee.cost()}")
from abc import ABC, abstractmethod class Beverage(ABC): @abstractmethod def cost(self): pass class Coffee(Beverage): def cost(self): return 5 class BeverageDecorator(Beverage): def __init__(self, beverage: Beverage): self.beverage = beverage def cost(self): return self.beverage.cost() class Milk(BeverageDecorator): def cost(self): return self.beverage.cost() + 2 class Sugar(BeverageDecorator): def cost(self): return self.beverage.cost() + 1 class Whip(BeverageDecorator): def cost(self): return self.beverage.cost() + 3 # Usage - Flexible composition coffee = Coffee() coffee = Milk(coffee) coffee = Sugar(coffee) coffee = Whip(coffee) print(f"Cost: ${coffee.cost()}") # $11 # Can create any combination dynamically double_milk_coffee = Milk(Milk(Coffee())) print(f"Double milk: ${double_milk_coffee.cost()}") # $9
from abc import ABC, abstractmethod class Beverage(ABC): @abstractmethod def cost(self): pass class Coffee(Beverage): def cost(self): return 5 class BeverageDecorator(Beverage): def __init__(self, beverage: Beverage): self.beverage = beverage def cost(self): return self.beverage.cost() class Milk(BeverageDecorator): def cost(self): return self.beverage.cost() + 2 class Sugar(BeverageDecorator): def cost(self): return self.beverage.cost() + 1 class Whip(BeverageDecorator): def cost(self): return self.beverage.cost() + 3 # Usage - Flexible composition coffee = Coffee() coffee = Milk(coffee) coffee = Sugar(coffee) coffee = Whip(coffee) print(f"Cost: ${coffee.cost()}") # $11 # Can create any combination dynamically double_milk_coffee = Milk(Milk(Coffee())) print(f"Double milk: ${double_milk_coffee.cost()}") # $9
from abc import ABC, abstractmethod class Beverage(ABC): @abstractmethod def cost(self): pass class Coffee(Beverage): def cost(self): return 5 class BeverageDecorator(Beverage): def __init__(self, beverage: Beverage): self.beverage = beverage def cost(self): return self.beverage.cost() class Milk(BeverageDecorator): def cost(self): return self.beverage.cost() + 2 class Sugar(BeverageDecorator): def cost(self): return self.beverage.cost() + 1 class Whip(BeverageDecorator): def cost(self): return self.beverage.cost() + 3 # Usage - Flexible composition coffee = Coffee() coffee = Milk(coffee) coffee = Sugar(coffee) coffee = Whip(coffee) print(f"Cost: ${coffee.cost()}") # $11 # Can create any combination dynamically double_milk_coffee = Milk(Milk(Coffee())) print(f"Double milk: ${double_milk_coffee.cost()}") # $9
from abc import ABC, abstractmethod class Beverage(ABC): @abstractmethod def cost(self): pass class Coffee(Beverage): def cost(self): return 5 class BeverageDecorator(Beverage): def __init__(self, beverage: Beverage): self.beverage = beverage def cost(self): return self.beverage.cost() class Milk(BeverageDecorator): def cost(self): return self.beverage.cost() + 2 class Sugar(BeverageDecorator): def cost(self): return self.beverage.cost() + 1 class Whip(BeverageDecorator): def cost(self): return self.beverage.cost() + 3 # Usage - Flexible composition coffee = Coffee() coffee = Milk(coffee) coffee = Sugar(coffee) coffee = Whip(coffee) print(f"Cost: ${coffee.cost()}") # $11 # Can create any combination dynamically double_milk_coffee = Milk(Milk(Coffee())) print(f"Double milk: ${double_milk_coffee.cost()}") # $9
from abc import ABC, abstractmethod class Beverage(ABC): @abstractmethod def cost(self): pass class Coffee(Beverage): def cost(self): return 5 class BeverageDecorator(Beverage): def __init__(self, beverage: Beverage): self.beverage = beverage def cost(self): return self.beverage.cost() class Milk(BeverageDecorator): def cost(self): return self.beverage.cost() + 2 class Sugar(BeverageDecorator): def cost(self): return self.beverage.cost() + 1 class Whip(BeverageDecorator): def cost(self): return self.beverage.cost() + 3 # Usage - Flexible composition coffee = Coffee() coffee = Milk(coffee) coffee = Sugar(coffee) coffee = Whip(coffee) print(f"Cost: ${coffee.cost()}") # $11 # Can create any combination dynamically double_milk_coffee = Milk(Milk(Coffee())) print(f"Double milk: ${double_milk_coffee.cost()}") # $9
from abc import ABC, abstractmethod class Beverage(ABC): @abstractmethod def cost(self): pass class Coffee(Beverage): def cost(self): return 5 class BeverageDecorator(Beverage): def __init__(self, beverage: Beverage): self.beverage = beverage def cost(self): return self.beverage.cost() class Milk(BeverageDecorator): def cost(self): return self.beverage.cost() + 2 class Sugar(BeverageDecorator): def cost(self): return self.beverage.cost() + 1 class Whip(BeverageDecorator): def cost(self): return self.beverage.cost() + 3 # Usage - Flexible composition coffee = Coffee() coffee = Milk(coffee) coffee = Sugar(coffee) coffee = Whip(coffee) print(f"Cost: ${coffee.cost()}") # $11 # Can create any combination dynamically double_milk_coffee = Milk(Milk(Coffee())) print(f"Double milk: ${double_milk_coffee.cost()}") # $9
# Manual decoration def greet(name): return f"Hello, {name}!" # Add logging manually def greet_with_logging(name): print(f"Calling greet with {name}") result = f"Hello, {name}!" print(f"Returned: {result}") return result # Add timing manually import time def greet_with_timing(name): start = time.time() result = f"Hello, {name}!" end = time.time() print(f"Took {end - start:.4f} seconds") return result # Problem: Lots of duplicate code # What if we want both logging AND timing?
# Manual decoration def greet(name): return f"Hello, {name}!" # Add logging manually def greet_with_logging(name): print(f"Calling greet with {name}") result = f"Hello, {name}!" print(f"Returned: {result}") return result # Add timing manually import time def greet_with_timing(name): start = time.time() result = f"Hello, {name}!" end = time.time() print(f"Took {end - start:.4f} seconds") return result # Problem: Lots of duplicate code # What if we want both logging AND timing?
# Manual decoration def greet(name): return f"Hello, {name}!" # Add logging manually def greet_with_logging(name): print(f"Calling greet with {name}") result = f"Hello, {name}!" print(f"Returned: {result}") return result # Add timing manually import time def greet_with_timing(name): start = time.time() result = f"Hello, {name}!" end = time.time() print(f"Took {end - start:.4f} seconds") return result # Problem: Lots of duplicate code # What if we want both logging AND timing?
# Manual decoration def greet(name): return f"Hello, {name}!" # Add logging manually def greet_with_logging(name): print(f"Calling greet with {name}") result = f"Hello, {name}!" print(f"Returned: {result}") return result # Add timing manually import time def greet_with_timing(name): start = time.time() result = f"Hello, {name}!" end = time.time() print(f"Took {end - start:.4f} seconds") return result # Problem: Lots of duplicate code # What if we want both logging AND timing?
# Manual decoration def greet(name): return f"Hello, {name}!" # Add logging manually def greet_with_logging(name): print(f"Calling greet with {name}") result = f"Hello, {name}!" print(f"Returned: {result}") return result # Add timing manually import time def greet_with_timing(name): start = time.time() result = f"Hello, {name}!" end = time.time() print(f"Took {end - start:.4f} seconds") return result # Problem: Lots of duplicate code # What if we want both logging AND timing?
# Decorator functions def log_calls(func): def wrapper(*args, **kwargs): print(f"Calling {func.__name__} with {args}") result = func(*args, **kwargs) print(f"Returned: {result}") return result return wrapper def time_it(func): def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(f"{func.__name__} took {end - start:.4f} seconds") return result return wrapper # Apply decorators @log_calls @time_it def greet(name): return f"Hello, {name}!" # Usage greet("Alice") # Calling greet with ('Alice',) # greet took 0.0001 seconds # Returned: Hello, Alice! # Decorators compose - order matters! @time_it @log_calls def farewell(name): return f"Goodbye, {name}!"
# Decorator functions def log_calls(func): def wrapper(*args, **kwargs): print(f"Calling {func.__name__} with {args}") result = func(*args, **kwargs) print(f"Returned: {result}") return result return wrapper def time_it(func): def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(f"{func.__name__} took {end - start:.4f} seconds") return result return wrapper # Apply decorators @log_calls @time_it def greet(name): return f"Hello, {name}!" # Usage greet("Alice") # Calling greet with ('Alice',) # greet took 0.0001 seconds # Returned: Hello, Alice! # Decorators compose - order matters! @time_it @log_calls def farewell(name): return f"Goodbye, {name}!"
# Decorator functions def log_calls(func): def wrapper(*args, **kwargs): print(f"Calling {func.__name__} with {args}") result = func(*args, **kwargs) print(f"Returned: {result}") return result return wrapper def time_it(func): def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(f"{func.__name__} took {end - start:.4f} seconds") return result return wrapper # Apply decorators @log_calls @time_it def greet(name): return f"Hello, {name}!" # Usage greet("Alice") # Calling greet with ('Alice',) # greet took 0.0001 seconds # Returned: Hello, Alice! # Decorators compose - order matters! @time_it @log_calls def farewell(name): return f"Goodbye, {name}!"
# Decorator functions def log_calls(func): def wrapper(*args, **kwargs): print(f"Calling {func.__name__} with {args}") result = func(*args, **kwargs) print(f"Returned: {result}") return result return wrapper def time_it(func): def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(f"{func.__name__} took {end - start:.4f} seconds") return result return wrapper # Apply decorators @log_calls @time_it def greet(name): return f"Hello, {name}!" # Usage greet("Alice") # Calling greet with ('Alice',) # greet took 0.0001 seconds # Returned: Hello, Alice! # Decorators compose - order matters! @time_it @log_calls def farewell(name): return f"Goodbye, {name}!"
# Decorator functions def log_calls(func): def wrapper(*args, **kwargs): print(f"Calling {func.__name__} with {args}") result = func(*args, **kwargs) print(f"Returned: {result}") return result return wrapper def time_it(func): def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(f"{func.__name__} took {end - start:.4f} seconds") return result return wrapper # Apply decorators @log_calls @time_it def greet(name): return f"Hello, {name}!" # Usage greet("Alice") # Calling greet with ('Alice',) # greet took 0.0001 seconds # Returned: Hello, Alice! # Decorators compose - order matters! @time_it @log_calls def farewell(name): return f"Goodbye, {name}!"
// Repetitive authentication logic function UserProfile({ user }) { if (!user) { return <div>Please login to view profile</div>; } return ( <div> <h1>{user.name}</h1> <p>{user.email}</p> </div> ); } function UserSettings({ user }) { if (!user) { return <div>Please login to access settings</div>; } return ( <div> <h2>Settings for {user.name}</h2> {/* Settings UI */} </div> ); } // Problem: Duplicate auth check in every component
// Repetitive authentication logic function UserProfile({ user }) { if (!user) { return <div>Please login to view profile</div>; } return ( <div> <h1>{user.name}</h1> <p>{user.email}</p> </div> ); } function UserSettings({ user }) { if (!user) { return <div>Please login to access settings</div>; } return ( <div> <h2>Settings for {user.name}</h2> {/* Settings UI */} </div> ); } // Problem: Duplicate auth check in every component
// Repetitive authentication logic function UserProfile({ user }) { if (!user) { return <div>Please login to view profile</div>; } return ( <div> <h1>{user.name}</h1> <p>{user.email}</p> </div> ); } function UserSettings({ user }) { if (!user) { return <div>Please login to access settings</div>; } return ( <div> <h2>Settings for {user.name}</h2> {/* Settings UI */} </div> ); } // Problem: Duplicate auth check in every component
// Higher-Order Component function withAuth(WrappedComponent) { return function AuthenticatedComponent(props) { const { user } = props; if (!user) { return <div>Please login to continue</div>; } // Pass through all props to wrapped component return <WrappedComponent {...props} />; }; } // Clean components without auth logic function UserProfile({ user }) { return ( <div> <h1>{user.name}</h1> <p>{user.email}</p> </div> ); } function UserSettings({ user }) { return ( <div> <h2>Settings for {user.name}</h2> {/* Settings UI */} </div> ); } // Apply the HoC const ProtectedProfile = withAuth(UserProfile); const ProtectedSettings = withAuth(UserSettings); // Usage <ProtectedProfile user={currentUser} /> <ProtectedSettings user={currentUser} />
// Higher-Order Component function withAuth(WrappedComponent) { return function AuthenticatedComponent(props) { const { user } = props; if (!user) { return <div>Please login to continue</div>; } // Pass through all props to wrapped component return <WrappedComponent {...props} />; }; } // Clean components without auth logic function UserProfile({ user }) { return ( <div> <h1>{user.name}</h1> <p>{user.email}</p> </div> ); } function UserSettings({ user }) { return ( <div> <h2>Settings for {user.name}</h2> {/* Settings UI */} </div> ); } // Apply the HoC const ProtectedProfile = withAuth(UserProfile); const ProtectedSettings = withAuth(UserSettings); // Usage <ProtectedProfile user={currentUser} /> <ProtectedSettings user={currentUser} />
// Higher-Order Component function withAuth(WrappedComponent) { return function AuthenticatedComponent(props) { const { user } = props; if (!user) { return <div>Please login to continue</div>; } // Pass through all props to wrapped component return <WrappedComponent {...props} />; }; } // Clean components without auth logic function UserProfile({ user }) { return ( <div> <h1>{user.name}</h1> <p>{user.email}</p> </div> ); } function UserSettings({ user }) { return ( <div> <h2>Settings for {user.name}</h2> {/* Settings UI */} </div> ); } // Apply the HoC const ProtectedProfile = withAuth(UserProfile); const ProtectedSettings = withAuth(UserSettings); // Usage <ProtectedProfile user={currentUser} /> <ProtectedSettings user={currentUser} />
// Higher-Order Component function withAuth(WrappedComponent) { return function AuthenticatedComponent(props) { const { user } = props; if (!user) { return <div>Please login to continue</div>; } // Pass through all props to wrapped component return <WrappedComponent {...props} />; }; } // Clean components without auth logic function UserProfile({ user }) { return ( <div> <h1>{user.name}</h1> <p>{user.email}</p> </div> ); } function UserSettings({ user }) { return ( <div> <h2>Settings for {user.name}</h2> {/* Settings UI */} </div> ); } // Apply the HoC const ProtectedProfile = withAuth(UserProfile); const ProtectedSettings = withAuth(UserSettings); // Usage <ProtectedProfile user={currentUser} /> <ProtectedSettings user={currentUser} />
// Higher-Order Component function withAuth(WrappedComponent) { return function AuthenticatedComponent(props) { const { user } = props; if (!user) { return <div>Please login to continue</div>; } // Pass through all props to wrapped component return <WrappedComponent {...props} />; }; } // Clean components without auth logic function UserProfile({ user }) { return ( <div> <h1>{user.name}</h1> <p>{user.email}</p> </div> ); } function UserSettings({ user }) { return ( <div> <h2>Settings for {user.name}</h2> {/* Settings UI */} </div> ); } // Apply the HoC const ProtectedProfile = withAuth(UserProfile); const ProtectedSettings = withAuth(UserSettings); // Usage <ProtectedProfile user={currentUser} /> <ProtectedSettings user={currentUser} />
Use the Proxy pattern when:
import requests import time class WeatherAPI: def __init__(self): self.api_key = "secret_key" def get_weather(self, city): print(f"Fetching weather for {city}...") # Simulated API call time.sleep(1) # Slow network call return f"Sunny in {city}, 25°C" # Direct usage api = WeatherAPI() # Problem: Every call hits the API print(api.get_weather("London")) # Slow print(api.get_weather("London")) # Slow again! print(api.get_weather("London")) # Still slow!
import requests import time class WeatherAPI: def __init__(self): self.api_key = "secret_key" def get_weather(self, city): print(f"Fetching weather for {city}...") # Simulated API call time.sleep(1) # Slow network call return f"Sunny in {city}, 25°C" # Direct usage api = WeatherAPI() # Problem: Every call hits the API print(api.get_weather("London")) # Slow print(api.get_weather("London")) # Slow again! print(api.get_weather("London")) # Still slow!
import requests import time class WeatherAPI: def __init__(self): self.api_key = "secret_key" def get_weather(self, city): print(f"Fetching weather for {city}...") # Simulated API call time.sleep(1) # Slow network call return f"Sunny in {city}, 25°C" # Direct usage api = WeatherAPI() # Problem: Every call hits the API print(api.get_weather("London")) # Slow print(api.get_weather("London")) # Slow again! print(api.get_weather("London")) # Still slow!
import requests import time from datetime import datetime, timedelta class WeatherAPI: def __init__(self): self.api_key = "secret_key" def get_weather(self, city): print(f"Fetching weather for {city}...") # Simulated API call time.sleep(1) # Slow network call return f"Sunny in {city}, 25°C" class CachedWeatherProxy: def __init__(self, weather_api): self.api = weather_api self.cache = {} self.cache_duration = timedelta(minutes=10) def get_weather(self, city): # Check cache first if city in self.cache: cached_data, timestamp = self.cache[city] if datetime.now() - timestamp < self.cache_duration: print(f"Returning cached weather for {city}") return cached_data # Cache miss - call real API result = self.api.get_weather(city) self.cache[city] = (result, datetime.now()) return result # Usage with proxy api = WeatherAPI() proxy = CachedWeatherProxy(api) print(proxy.get_weather("London")) # Slow (cache miss) print(proxy.get_weather("London")) # Fast (cache hit) print(proxy.get_weather("London")) # Fast (cache hit)
import requests import time from datetime import datetime, timedelta class WeatherAPI: def __init__(self): self.api_key = "secret_key" def get_weather(self, city): print(f"Fetching weather for {city}...") # Simulated API call time.sleep(1) # Slow network call return f"Sunny in {city}, 25°C" class CachedWeatherProxy: def __init__(self, weather_api): self.api = weather_api self.cache = {} self.cache_duration = timedelta(minutes=10) def get_weather(self, city): # Check cache first if city in self.cache: cached_data, timestamp = self.cache[city] if datetime.now() - timestamp < self.cache_duration: print(f"Returning cached weather for {city}") return cached_data # Cache miss - call real API result = self.api.get_weather(city) self.cache[city] = (result, datetime.now()) return result # Usage with proxy api = WeatherAPI() proxy = CachedWeatherProxy(api) print(proxy.get_weather("London")) # Slow (cache miss) print(proxy.get_weather("London")) # Fast (cache hit) print(proxy.get_weather("London")) # Fast (cache hit)
import requests import time from datetime import datetime, timedelta class WeatherAPI: def __init__(self): self.api_key = "secret_key" def get_weather(self, city): print(f"Fetching weather for {city}...") # Simulated API call time.sleep(1) # Slow network call return f"Sunny in {city}, 25°C" class CachedWeatherProxy: def __init__(self, weather_api): self.api = weather_api self.cache = {} self.cache_duration = timedelta(minutes=10) def get_weather(self, city): # Check cache first if city in self.cache: cached_data, timestamp = self.cache[city] if datetime.now() - timestamp < self.cache_duration: print(f"Returning cached weather for {city}") return cached_data # Cache miss - call real API result = self.api.get_weather(city) self.cache[city] = (result, datetime.now()) return result # Usage with proxy api = WeatherAPI() proxy = CachedWeatherProxy(api) print(proxy.get_weather("London")) # Slow (cache miss) print(proxy.get_weather("London")) # Fast (cache hit) print(proxy.get_weather("London")) # Fast (cache hit)
import requests import time from datetime import datetime, timedelta class WeatherAPI: def __init__(self): self.api_key = "secret_key" def get_weather(self, city): print(f"Fetching weather for {city}...") # Simulated API call time.sleep(1) # Slow network call return f"Sunny in {city}, 25°C" class CachedWeatherProxy: def __init__(self, weather_api): self.api = weather_api self.cache = {} self.cache_duration = timedelta(minutes=10) def get_weather(self, city): # Check cache first if city in self.cache: cached_data, timestamp = self.cache[city] if datetime.now() - timestamp < self.cache_duration: print(f"Returning cached weather for {city}") return cached_data # Cache miss - call real API result = self.api.get_weather(city) self.cache[city] = (result, datetime.now()) return result # Usage with proxy api = WeatherAPI() proxy = CachedWeatherProxy(api) print(proxy.get_weather("London")) # Slow (cache miss) print(proxy.get_weather("London")) # Fast (cache hit) print(proxy.get_weather("London")) # Fast (cache hit)
import requests import time from datetime import datetime, timedelta class WeatherAPI: def __init__(self): self.api_key = "secret_key" def get_weather(self, city): print(f"Fetching weather for {city}...") # Simulated API call time.sleep(1) # Slow network call return f"Sunny in {city}, 25°C" class CachedWeatherProxy: def __init__(self, weather_api): self.api = weather_api self.cache = {} self.cache_duration = timedelta(minutes=10) def get_weather(self, city): # Check cache first if city in self.cache: cached_data, timestamp = self.cache[city] if datetime.now() - timestamp < self.cache_duration: print(f"Returning cached weather for {city}") return cached_data # Cache miss - call real API result = self.api.get_weather(city) self.cache[city] = (result, datetime.now()) return result # Usage with proxy api = WeatherAPI() proxy = CachedWeatherProxy(api) print(proxy.get_weather("London")) # Slow (cache miss) print(proxy.get_weather("London")) # Fast (cache hit) print(proxy.get_weather("London")) # Fast (cache hit)
import requests import time from datetime import datetime, timedelta class WeatherAPI: def __init__(self): self.api_key = "secret_key" def get_weather(self, city): print(f"Fetching weather for {city}...") # Simulated API call time.sleep(1) # Slow network call return f"Sunny in {city}, 25°C" class CachedWeatherProxy: def __init__(self, weather_api): self.api = weather_api self.cache = {} self.cache_duration = timedelta(minutes=10) def get_weather(self, city): # Check cache first if city in self.cache: cached_data, timestamp = self.cache[city] if datetime.now() - timestamp < self.cache_duration: print(f"Returning cached weather for {city}") return cached_data # Cache miss - call real API result = self.api.get_weather(city) self.cache[city] = (result, datetime.now()) return result # Usage with proxy api = WeatherAPI() proxy = CachedWeatherProxy(api) print(proxy.get_weather("London")) # Slow (cache miss) print(proxy.get_weather("London")) # Fast (cache hit) print(proxy.get_weather("London")) # Fast (cache hit)
Use the Adapter pattern when:
# Different payment gateways with different interfaces class PayPalGateway: def make_payment(self, amount): print(f"PayPal: Processing ${amount}") return {"status": "success", "transaction_id": "PP123"} def get_balance(self): return 1000.00 class StripeGateway: def charge(self, amount_cents): print(f"Stripe: Charging {amount_cents} cents") return {"outcome": "succeeded", "id": "ST456"} def retrieve_balance(self): return {"available": 150000} # in cents # Problem: Different interfaces for same functionality # Client code needs to know specifics of each gateway # PayPal uses dollars paypal = PayPalGateway() paypal.make_payment(100) # Stripe uses cents and different method names stripe = StripeGateway() stripe.charge(10000) # $100 in cents
# Different payment gateways with different interfaces class PayPalGateway: def make_payment(self, amount): print(f"PayPal: Processing ${amount}") return {"status": "success", "transaction_id": "PP123"} def get_balance(self): return 1000.00 class StripeGateway: def charge(self, amount_cents): print(f"Stripe: Charging {amount_cents} cents") return {"outcome": "succeeded", "id": "ST456"} def retrieve_balance(self): return {"available": 150000} # in cents # Problem: Different interfaces for same functionality # Client code needs to know specifics of each gateway # PayPal uses dollars paypal = PayPalGateway() paypal.make_payment(100) # Stripe uses cents and different method names stripe = StripeGateway() stripe.charge(10000) # $100 in cents
# Different payment gateways with different interfaces class PayPalGateway: def make_payment(self, amount): print(f"PayPal: Processing ${amount}") return {"status": "success", "transaction_id": "PP123"} def get_balance(self): return 1000.00 class StripeGateway: def charge(self, amount_cents): print(f"Stripe: Charging {amount_cents} cents") return {"outcome": "succeeded", "id": "ST456"} def retrieve_balance(self): return {"available": 150000} # in cents # Problem: Different interfaces for same functionality # Client code needs to know specifics of each gateway # PayPal uses dollars paypal = PayPalGateway() paypal.make_payment(100) # Stripe uses cents and different method names stripe = StripeGateway() stripe.charge(10000) # $100 in cents
# Different payment gateways with different interfaces class PayPalGateway: def make_payment(self, amount): print(f"PayPal: Processing ${amount}") return {"status": "success", "transaction_id": "PP123"} def get_balance(self): return 1000.00 class StripeGateway: def charge(self, amount_cents): print(f"Stripe: Charging {amount_cents} cents") return {"outcome": "succeeded", "id": "ST456"} def retrieve_balance(self): return {"available": 150000} # in cents # Problem: Different interfaces for same functionality # Client code needs to know specifics of each gateway # PayPal uses dollars paypal = PayPalGateway() paypal.make_payment(100) # Stripe uses cents and different method names stripe = StripeGateway() stripe.charge(10000) # $100 in cents
# Different payment gateways with different interfaces class PayPalGateway: def make_payment(self, amount): print(f"PayPal: Processing ${amount}") return {"status": "success", "transaction_id": "PP123"} def get_balance(self): return 1000.00 class StripeGateway: def charge(self, amount_cents): print(f"Stripe: Charging {amount_cents} cents") return {"outcome": "succeeded", "id": "ST456"} def retrieve_balance(self): return {"available": 150000} # in cents # Problem: Different interfaces for same functionality # Client code needs to know specifics of each gateway # PayPal uses dollars paypal = PayPalGateway() paypal.make_payment(100) # Stripe uses cents and different method names stripe = StripeGateway() stripe.charge(10000) # $100 in cents
# Different payment gateways with different interfaces class PayPalGateway: def make_payment(self, amount): print(f"PayPal: Processing ${amount}") return {"status": "success", "transaction_id": "PP123"} def get_balance(self): return 1000.00 class StripeGateway: def charge(self, amount_cents): print(f"Stripe: Charging {amount_cents} cents") return {"outcome": "succeeded", "id": "ST456"} def retrieve_balance(self): return {"available": 150000} # in cents # Problem: Different interfaces for same functionality # Client code needs to know specifics of each gateway # PayPal uses dollars paypal = PayPalGateway() paypal.make_payment(100) # Stripe uses cents and different method names stripe = StripeGateway() stripe.charge(10000) # $100 in cents
from abc import ABC, abstractmethod # Common interface class PaymentGateway(ABC): @abstractmethod def process_payment(self, amount): pass @abstractmethod def get_balance(self): pass # Adapter for PayPal class PayPalAdapter(PaymentGateway): def __init__(self): self.gateway = PayPalGateway() def process_payment(self, amount): result = self.gateway.make_payment(amount) return { "success": result["status"] == "success", "transaction_id": result["transaction_id"] } def get_balance(self): return self.gateway.get_balance() # Adapter for Stripe class StripeAdapter(PaymentGateway): def __init__(self): self.gateway = StripeGateway() def process_payment(self, amount): amount_cents = int(amount * 100) result = self.gateway.charge(amount_cents) return { "success": result["outcome"] == "succeeded", "transaction_id": result["id"] } def get_balance(self): balance_cents = self.gateway.retrieve_balance()["available"] return balance_cents / 100 # Uniform usage gateways = { "paypal": PayPalAdapter(), "stripe": StripeAdapter() } for name, gateway in gateways.items(): print(f"\n{name}:") gateway.process_payment(100) print(f"Balance: ${gateway.get_balance()}")
from abc import ABC, abstractmethod # Common interface class PaymentGateway(ABC): @abstractmethod def process_payment(self, amount): pass @abstractmethod def get_balance(self): pass # Adapter for PayPal class PayPalAdapter(PaymentGateway): def __init__(self): self.gateway = PayPalGateway() def process_payment(self, amount): result = self.gateway.make_payment(amount) return { "success": result["status"] == "success", "transaction_id": result["transaction_id"] } def get_balance(self): return self.gateway.get_balance() # Adapter for Stripe class StripeAdapter(PaymentGateway): def __init__(self): self.gateway = StripeGateway() def process_payment(self, amount): amount_cents = int(amount * 100) result = self.gateway.charge(amount_cents) return { "success": result["outcome"] == "succeeded", "transaction_id": result["id"] } def get_balance(self): balance_cents = self.gateway.retrieve_balance()["available"] return balance_cents / 100 # Uniform usage gateways = { "paypal": PayPalAdapter(), "stripe": StripeAdapter() } for name, gateway in gateways.items(): print(f"\n{name}:") gateway.process_payment(100) print(f"Balance: ${gateway.get_balance()}")
from abc import ABC, abstractmethod # Common interface class PaymentGateway(ABC): @abstractmethod def process_payment(self, amount): pass @abstractmethod def get_balance(self): pass # Adapter for PayPal class PayPalAdapter(PaymentGateway): def __init__(self): self.gateway = PayPalGateway() def process_payment(self, amount): result = self.gateway.make_payment(amount) return { "success": result["status"] == "success", "transaction_id": result["transaction_id"] } def get_balance(self): return self.gateway.get_balance() # Adapter for Stripe class StripeAdapter(PaymentGateway): def __init__(self): self.gateway = StripeGateway() def process_payment(self, amount): amount_cents = int(amount * 100) result = self.gateway.charge(amount_cents) return { "success": result["outcome"] == "succeeded", "transaction_id": result["id"] } def get_balance(self): balance_cents = self.gateway.retrieve_balance()["available"] return balance_cents / 100 # Uniform usage gateways = { "paypal": PayPalAdapter(), "stripe": StripeAdapter() } for name, gateway in gateways.items(): print(f"\n{name}:") gateway.process_payment(100) print(f"Balance: ${gateway.get_balance()}")
from abc import ABC, abstractmethod # Common interface class PaymentGateway(ABC): @abstractmethod def process_payment(self, amount): pass @abstractmethod def get_balance(self): pass # Adapter for PayPal class PayPalAdapter(PaymentGateway): def __init__(self): self.gateway = PayPalGateway() def process_payment(self, amount): result = self.gateway.make_payment(amount) return { "success": result["status"] == "success", "transaction_id": result["transaction_id"] } def get_balance(self): return self.gateway.get_balance() # Adapter for Stripe class StripeAdapter(PaymentGateway): def __init__(self): self.gateway = StripeGateway() def process_payment(self, amount): amount_cents = int(amount * 100) result = self.gateway.charge(amount_cents) return { "success": result["outcome"] == "succeeded", "transaction_id": result["id"] } def get_balance(self): balance_cents = self.gateway.retrieve_balance()["available"] return balance_cents / 100 # Uniform usage gateways = { "paypal": PayPalAdapter(), "stripe": StripeAdapter() } for name, gateway in gateways.items(): print(f"\n{name}:") gateway.process_payment(100) print(f"Balance: ${gateway.get_balance()}")
from abc import ABC, abstractmethod # Common interface class PaymentGateway(ABC): @abstractmethod def process_payment(self, amount): pass @abstractmethod def get_balance(self): pass # Adapter for PayPal class PayPalAdapter(PaymentGateway): def __init__(self): self.gateway = PayPalGateway() def process_payment(self, amount): result = self.gateway.make_payment(amount) return { "success": result["status"] == "success", "transaction_id": result["transaction_id"] } def get_balance(self): return self.gateway.get_balance() # Adapter for Stripe class StripeAdapter(PaymentGateway): def __init__(self): self.gateway = StripeGateway() def process_payment(self, amount): amount_cents = int(amount * 100) result = self.gateway.charge(amount_cents) return { "success": result["outcome"] == "succeeded", "transaction_id": result["id"] } def get_balance(self): balance_cents = self.gateway.retrieve_balance()["available"] return balance_cents / 100 # Uniform usage gateways = { "paypal": PayPalAdapter(), "stripe": StripeAdapter() } for name, gateway in gateways.items(): print(f"\n{name}:") gateway.process_payment(100) print(f"Balance: ${gateway.get_balance()}")
Use the Composite pattern when:
class File: def __init__(self, name, size): self.name = name self.size = size def get_size(self): return self.size class Directory: def __init__(self, name): self.name = name self.files = [] self.directories = [] def add_file(self, file): self.files.append(file) def add_directory(self, directory): self.directories.append(directory) def get_size(self): # Complex logic to handle both files and directories total = 0 for file in self.files: total += file.get_size() for directory in self.directories: total += directory.get_size() return total # Usage is awkward - different methods for files vs directories root = Directory("root") root.add_file(File("readme.txt", 100)) root.add_directory(Directory("src"))
class File: def __init__(self, name, size): self.name = name self.size = size def get_size(self): return self.size class Directory: def __init__(self, name): self.name = name self.files = [] self.directories = [] def add_file(self, file): self.files.append(file) def add_directory(self, directory): self.directories.append(directory) def get_size(self): # Complex logic to handle both files and directories total = 0 for file in self.files: total += file.get_size() for directory in self.directories: total += directory.get_size() return total # Usage is awkward - different methods for files vs directories root = Directory("root") root.add_file(File("readme.txt", 100)) root.add_directory(Directory("src"))
class File: def __init__(self, name, size): self.name = name self.size = size def get_size(self): return self.size class Directory: def __init__(self, name): self.name = name self.files = [] self.directories = [] def add_file(self, file): self.files.append(file) def add_directory(self, directory): self.directories.append(directory) def get_size(self): # Complex logic to handle both files and directories total = 0 for file in self.files: total += file.get_size() for directory in self.directories: total += directory.get_size() return total # Usage is awkward - different methods for files vs directories root = Directory("root") root.add_file(File("readme.txt", 100)) root.add_directory(Directory("src"))
class File: def __init__(self, name, size): self.name = name self.size = size def get_size(self): return self.size class Directory: def __init__(self, name): self.name = name self.files = [] self.directories = [] def add_file(self, file): self.files.append(file) def add_directory(self, directory): self.directories.append(directory) def get_size(self): # Complex logic to handle both files and directories total = 0 for file in self.files: total += file.get_size() for directory in self.directories: total += directory.get_size() return total # Usage is awkward - different methods for files vs directories root = Directory("root") root.add_file(File("readme.txt", 100)) root.add_directory(Directory("src"))
class File: def __init__(self, name, size): self.name = name self.size = size def get_size(self): return self.size class Directory: def __init__(self, name): self.name = name self.files = [] self.directories = [] def add_file(self, file): self.files.append(file) def add_directory(self, directory): self.directories.append(directory) def get_size(self): # Complex logic to handle both files and directories total = 0 for file in self.files: total += file.get_size() for directory in self.directories: total += directory.get_size() return total # Usage is awkward - different methods for files vs directories root = Directory("root") root.add_file(File("readme.txt", 100)) root.add_directory(Directory("src"))
# Common interface class FileSystemItem: def __init__(self, name): self.name = name def get_size(self): raise NotImplementedError class File(FileSystemItem): def __init__(self, name, size): super().__init__(name) self._size = size def get_size(self): print(f"File '{self.name}': {self._size} bytes") return self._size class Directory(FileSystemItem): def __init__(self, name): super().__init__(name) self.children = [] # Can contain files OR directories def add(self, item): self.children.append(item) def get_size(self): total = sum(child.get_size() for child in self.children) print(f"Directory '{self.name}': {total} bytes total") return total # Clean, uniform usage root = Directory("root") root.add(File("readme.txt", 100)) src = Directory("src") src.add(File("main.py", 500)) src.add(File("utils.py", 300)) root.add(src) # Recursive calculation works seamlessly total_size = root.get_size() # File 'readme.txt': 100 bytes # File 'main.py': 500 bytes # File 'utils.py': 300 bytes # Directory 'src': 800 bytes total # Directory 'root': 900 bytes total
# Common interface class FileSystemItem: def __init__(self, name): self.name = name def get_size(self): raise NotImplementedError class File(FileSystemItem): def __init__(self, name, size): super().__init__(name) self._size = size def get_size(self): print(f"File '{self.name}': {self._size} bytes") return self._size class Directory(FileSystemItem): def __init__(self, name): super().__init__(name) self.children = [] # Can contain files OR directories def add(self, item): self.children.append(item) def get_size(self): total = sum(child.get_size() for child in self.children) print(f"Directory '{self.name}': {total} bytes total") return total # Clean, uniform usage root = Directory("root") root.add(File("readme.txt", 100)) src = Directory("src") src.add(File("main.py", 500)) src.add(File("utils.py", 300)) root.add(src) # Recursive calculation works seamlessly total_size = root.get_size() # File 'readme.txt': 100 bytes # File 'main.py': 500 bytes # File 'utils.py': 300 bytes # Directory 'src': 800 bytes total # Directory 'root': 900 bytes total
# Common interface class FileSystemItem: def __init__(self, name): self.name = name def get_size(self): raise NotImplementedError class File(FileSystemItem): def __init__(self, name, size): super().__init__(name) self._size = size def get_size(self): print(f"File '{self.name}': {self._size} bytes") return self._size class Directory(FileSystemItem): def __init__(self, name): super().__init__(name) self.children = [] # Can contain files OR directories def add(self, item): self.children.append(item) def get_size(self): total = sum(child.get_size() for child in self.children) print(f"Directory '{self.name}': {total} bytes total") return total # Clean, uniform usage root = Directory("root") root.add(File("readme.txt", 100)) src = Directory("src") src.add(File("main.py", 500)) src.add(File("utils.py", 300)) root.add(src) # Recursive calculation works seamlessly total_size = root.get_size() # File 'readme.txt': 100 bytes # File 'main.py': 500 bytes # File 'utils.py': 300 bytes # Directory 'src': 800 bytes total # Directory 'root': 900 bytes total
# Common interface class FileSystemItem: def __init__(self, name): self.name = name def get_size(self): raise NotImplementedError class File(FileSystemItem): def __init__(self, name, size): super().__init__(name) self._size = size def get_size(self): print(f"File '{self.name}': {self._size} bytes") return self._size class Directory(FileSystemItem): def __init__(self, name): super().__init__(name) self.children = [] # Can contain files OR directories def add(self, item): self.children.append(item) def get_size(self): total = sum(child.get_size() for child in self.children) print(f"Directory '{self.name}': {total} bytes total") return total # Clean, uniform usage root = Directory("root") root.add(File("readme.txt", 100)) src = Directory("src") src.add(File("main.py", 500)) src.add(File("utils.py", 300)) root.add(src) # Recursive calculation works seamlessly total_size = root.get_size() # File 'readme.txt': 100 bytes # File 'main.py': 500 bytes # File 'utils.py': 300 bytes # Directory 'src': 800 bytes total # Directory 'root': 900 bytes total
# Common interface class FileSystemItem: def __init__(self, name): self.name = name def get_size(self): raise NotImplementedError class File(FileSystemItem): def __init__(self, name, size): super().__init__(name) self._size = size def get_size(self): print(f"File '{self.name}': {self._size} bytes") return self._size class Directory(FileSystemItem): def __init__(self, name): super().__init__(name) self.children = [] # Can contain files OR directories def add(self, item): self.children.append(item) def get_size(self): total = sum(child.get_size() for child in self.children) print(f"Directory '{self.name}': {total} bytes total") return total # Clean, uniform usage root = Directory("root") root.add(File("readme.txt", 100)) src = Directory("src") src.add(File("main.py", 500)) src.add(File("utils.py", 300)) root.add(src) # Recursive calculation works seamlessly total_size = root.get_size() # File 'readme.txt': 100 bytes # File 'main.py': 500 bytes # File 'utils.py': 300 bytes # Directory 'src': 800 bytes total # Directory 'root': 900 bytes total
# Common interface class FileSystemItem: def __init__(self, name): self.name = name def get_size(self): raise NotImplementedError class File(FileSystemItem): def __init__(self, name, size): super().__init__(name) self._size = size def get_size(self): print(f"File '{self.name}': {self._size} bytes") return self._size class Directory(FileSystemItem): def __init__(self, name): super().__init__(name) self.children = [] # Can contain files OR directories def add(self, item): self.children.append(item) def get_size(self): total = sum(child.get_size() for child in self.children) print(f"Directory '{self.name}': {total} bytes total") return total # Clean, uniform usage root = Directory("root") root.add(File("readme.txt", 100)) src = Directory("src") src.add(File("main.py", 500)) src.add(File("utils.py", 300)) root.add(src) # Recursive calculation works seamlessly total_size = root.get_size() # File 'readme.txt': 100 bytes # File 'main.py': 500 bytes # File 'utils.py': 300 bytes # Directory 'src': 800 bytes total # Directory 'root': 900 bytes total
# Common interface class FileSystemItem: def __init__(self, name): self.name = name def get_size(self): raise NotImplementedError class File(FileSystemItem): def __init__(self, name, size): super().__init__(name) self._size = size def get_size(self): print(f"File '{self.name}': {self._size} bytes") return self._size class Directory(FileSystemItem): def __init__(self, name): super().__init__(name) self.children = [] # Can contain files OR directories def add(self, item): self.children.append(item) def get_size(self): total = sum(child.get_size() for child in self.children) print(f"Directory '{self.name}': {total} bytes total") return total # Clean, uniform usage root = Directory("root") root.add(File("readme.txt", 100)) src = Directory("src") src.add(File("main.py", 500)) src.add(File("utils.py", 300)) root.add(src) # Recursive calculation works seamlessly total_size = root.get_size() # File 'readme.txt': 100 bytes # File 'main.py': 500 bytes # File 'utils.py': 300 bytes # Directory 'src': 800 bytes total # Directory 'root': 900 bytes total
// Composite pattern in React - components can contain other components function Button({ children, onClick }) { return ( <button onClick={onClick}> {children} </button> ); } function Icon({ name }) { return <i className={`icon-${name}`} />; } function Text({ children }) { return <span>{children}</span>; } // Composite usage - Button can contain any combination of children function App() { return ( <div> {/* Leaf component */} <Button onClick={() => alert('clicked')}> Simple Text </Button> {/* Composite component */} <Button onClick={() => alert('saved')}> <Icon name="save" /> <Text>Save Document</Text> </Button> {/* Deeply nested composition */} <Button onClick={() => alert('complex')}> <div> <Icon name="folder" /> <Text>Files</Text> <div> <Icon name="arrow-down" /> </div> </div> </Button> </div> ); } // All components follow same interface - they're all React components // Can be composed in any configuration
// Composite pattern in React - components can contain other components function Button({ children, onClick }) { return ( <button onClick={onClick}> {children} </button> ); } function Icon({ name }) { return <i className={`icon-${name}`} />; } function Text({ children }) { return <span>{children}</span>; } // Composite usage - Button can contain any combination of children function App() { return ( <div> {/* Leaf component */} <Button onClick={() => alert('clicked')}> Simple Text </Button> {/* Composite component */} <Button onClick={() => alert('saved')}> <Icon name="save" /> <Text>Save Document</Text> </Button> {/* Deeply nested composition */} <Button onClick={() => alert('complex')}> <div> <Icon name="folder" /> <Text>Files</Text> <div> <Icon name="arrow-down" /> </div> </div> </Button> </div> ); } // All components follow same interface - they're all React components // Can be composed in any configuration
// Composite pattern in React - components can contain other components function Button({ children, onClick }) { return ( <button onClick={onClick}> {children} </button> ); } function Icon({ name }) { return <i className={`icon-${name}`} />; } function Text({ children }) { return <span>{children}</span>; } // Composite usage - Button can contain any combination of children function App() { return ( <div> {/* Leaf component */} <Button onClick={() => alert('clicked')}> Simple Text </Button> {/* Composite component */} <Button onClick={() => alert('saved')}> <Icon name="save" /> <Text>Save Document</Text> </Button> {/* Deeply nested composition */} <Button onClick={() => alert('complex')}> <div> <Icon name="folder" /> <Text>Files</Text> <div> <Icon name="arrow-down" /> </div> </div> </Button> </div> ); } // All components follow same interface - they're all React components // Can be composed in any configuration
// Composite pattern in React - components can contain other components function Button({ children, onClick }) { return ( <button onClick={onClick}> {children} </button> ); } function Icon({ name }) { return <i className={`icon-${name}`} />; } function Text({ children }) { return <span>{children}</span>; } // Composite usage - Button can contain any combination of children function App() { return ( <div> {/* Leaf component */} <Button onClick={() => alert('clicked')}> Simple Text </Button> {/* Composite component */} <Button onClick={() => alert('saved')}> <Icon name="save" /> <Text>Save Document</Text> </Button> {/* Deeply nested composition */} <Button onClick={() => alert('complex')}> <div> <Icon name="folder" /> <Text>Files</Text> <div> <Icon name="arrow-down" /> </div> </div> </Button> </div> ); } // All components follow same interface - they're all React components // Can be composed in any configuration
// Composite pattern in React - components can contain other components function Button({ children, onClick }) { return ( <button onClick={onClick}> {children} </button> ); } function Icon({ name }) { return <i className={`icon-${name}`} />; } function Text({ children }) { return <span>{children}</span>; } // Composite usage - Button can contain any combination of children function App() { return ( <div> {/* Leaf component */} <Button onClick={() => alert('clicked')}> Simple Text </Button> {/* Composite component */} <Button onClick={() => alert('saved')}> <Icon name="save" /> <Text>Save Document</Text> </Button> {/* Deeply nested composition */} <Button onClick={() => alert('complex')}> <div> <Icon name="folder" /> <Text>Files</Text> <div> <Icon name="arrow-down" /> </div> </div> </Button> </div> ); } // All components follow same interface - they're all React components // Can be composed in any configuration
Use the Chain of Responsibility pattern when:
# Without Chain of Responsibility - Complex if/else def handle_purchase(amount, user): if amount < 100: if user.is_vip: discount = 0.2 else: discount = 0.1 print(f"Small purchase: {discount*100}% discount") elif amount < 1000: if user.is_vip: discount = 0.3 else: discount = 0.15 print(f"Medium purchase: {discount*100}% discount") else: # Manager approval needed if user.credit_score > 700: print("Large purchase approved by system") else: print("Large purchase needs manual approval") # Hard to extend with new rules handle_purchase(50, regular_user) handle_purchase(500, vip_user)
# Without Chain of Responsibility - Complex if/else def handle_purchase(amount, user): if amount < 100: if user.is_vip: discount = 0.2 else: discount = 0.1 print(f"Small purchase: {discount*100}% discount") elif amount < 1000: if user.is_vip: discount = 0.3 else: discount = 0.15 print(f"Medium purchase: {discount*100}% discount") else: # Manager approval needed if user.credit_score > 700: print("Large purchase approved by system") else: print("Large purchase needs manual approval") # Hard to extend with new rules handle_purchase(50, regular_user) handle_purchase(500, vip_user)
# Without Chain of Responsibility - Complex if/else def handle_purchase(amount, user): if amount < 100: if user.is_vip: discount = 0.2 else: discount = 0.1 print(f"Small purchase: {discount*100}% discount") elif amount < 1000: if user.is_vip: discount = 0.3 else: discount = 0.15 print(f"Medium purchase: {discount*100}% discount") else: # Manager approval needed if user.credit_score > 700: print("Large purchase approved by system") else: print("Large purchase needs manual approval") # Hard to extend with new rules handle_purchase(50, regular_user) handle_purchase(500, vip_user)
from abc import ABC, abstractmethod class PurchaseHandler(ABC): def __init__(self): self.next_handler = None def set_next(self, handler): self.next_handler = handler return handler @abstractmethod def handle(self, amount, user): if self.next_handler: return self.next_handler.handle(amount, user) class SmallPurchaseHandler(PurchaseHandler): def handle(self, amount, user): if amount < 100: discount = 0.2 if user.is_vip else 0.1 print(f"Small purchase: {discount*100}% discount") else: super().handle(amount, user) class MediumPurchaseHandler(PurchaseHandler): def handle(self, amount, user): if amount < 1000: discount = 0.3 if user.is_vip else 0.15 print(f"Medium purchase: {discount*100}% discount") else: super().handle(amount, user) class LargePurchaseHandler(PurchaseHandler): def handle(self, amount, user): if user.credit_score > 700: print("Large purchase approved by system") else: print("Large purchase needs manual approval") # Build the chain small = SmallPurchaseHandler() medium = MediumPurchaseHandler() large = LargePurchaseHandler() small.set_next(medium).set_next(large) # Clean usage small.handle(50, regular_user) small.handle(500, vip_user) small.handle(5000, vip_user)
from abc import ABC, abstractmethod class PurchaseHandler(ABC): def __init__(self): self.next_handler = None def set_next(self, handler): self.next_handler = handler return handler @abstractmethod def handle(self, amount, user): if self.next_handler: return self.next_handler.handle(amount, user) class SmallPurchaseHandler(PurchaseHandler): def handle(self, amount, user): if amount < 100: discount = 0.2 if user.is_vip else 0.1 print(f"Small purchase: {discount*100}% discount") else: super().handle(amount, user) class MediumPurchaseHandler(PurchaseHandler): def handle(self, amount, user): if amount < 1000: discount = 0.3 if user.is_vip else 0.15 print(f"Medium purchase: {discount*100}% discount") else: super().handle(amount, user) class LargePurchaseHandler(PurchaseHandler): def handle(self, amount, user): if user.credit_score > 700: print("Large purchase approved by system") else: print("Large purchase needs manual approval") # Build the chain small = SmallPurchaseHandler() medium = MediumPurchaseHandler() large = LargePurchaseHandler() small.set_next(medium).set_next(large) # Clean usage small.handle(50, regular_user) small.handle(500, vip_user) small.handle(5000, vip_user)
from abc import ABC, abstractmethod class PurchaseHandler(ABC): def __init__(self): self.next_handler = None def set_next(self, handler): self.next_handler = handler return handler @abstractmethod def handle(self, amount, user): if self.next_handler: return self.next_handler.handle(amount, user) class SmallPurchaseHandler(PurchaseHandler): def handle(self, amount, user): if amount < 100: discount = 0.2 if user.is_vip else 0.1 print(f"Small purchase: {discount*100}% discount") else: super().handle(amount, user) class MediumPurchaseHandler(PurchaseHandler): def handle(self, amount, user): if amount < 1000: discount = 0.3 if user.is_vip else 0.15 print(f"Medium purchase: {discount*100}% discount") else: super().handle(amount, user) class LargePurchaseHandler(PurchaseHandler): def handle(self, amount, user): if user.credit_score > 700: print("Large purchase approved by system") else: print("Large purchase needs manual approval") # Build the chain small = SmallPurchaseHandler() medium = MediumPurchaseHandler() large = LargePurchaseHandler() small.set_next(medium).set_next(large) # Clean usage small.handle(50, regular_user) small.handle(500, vip_user) small.handle(5000, vip_user)
from abc import ABC, abstractmethod class PurchaseHandler(ABC): def __init__(self): self.next_handler = None def set_next(self, handler): self.next_handler = handler return handler @abstractmethod def handle(self, amount, user): if self.next_handler: return self.next_handler.handle(amount, user) class SmallPurchaseHandler(PurchaseHandler): def handle(self, amount, user): if amount < 100: discount = 0.2 if user.is_vip else 0.1 print(f"Small purchase: {discount*100}% discount") else: super().handle(amount, user) class MediumPurchaseHandler(PurchaseHandler): def handle(self, amount, user): if amount < 1000: discount = 0.3 if user.is_vip else 0.15 print(f"Medium purchase: {discount*100}% discount") else: super().handle(amount, user) class LargePurchaseHandler(PurchaseHandler): def handle(self, amount, user): if user.credit_score > 700: print("Large purchase approved by system") else: print("Large purchase needs manual approval") # Build the chain small = SmallPurchaseHandler() medium = MediumPurchaseHandler() large = LargePurchaseHandler() small.set_next(medium).set_next(large) # Clean usage small.handle(50, regular_user) small.handle(500, vip_user) small.handle(5000, vip_user)
from abc import ABC, abstractmethod class PurchaseHandler(ABC): def __init__(self): self.next_handler = None def set_next(self, handler): self.next_handler = handler return handler @abstractmethod def handle(self, amount, user): if self.next_handler: return self.next_handler.handle(amount, user) class SmallPurchaseHandler(PurchaseHandler): def handle(self, amount, user): if amount < 100: discount = 0.2 if user.is_vip else 0.1 print(f"Small purchase: {discount*100}% discount") else: super().handle(amount, user) class MediumPurchaseHandler(PurchaseHandler): def handle(self, amount, user): if amount < 1000: discount = 0.3 if user.is_vip else 0.15 print(f"Medium purchase: {discount*100}% discount") else: super().handle(amount, user) class LargePurchaseHandler(PurchaseHandler): def handle(self, amount, user): if user.credit_score > 700: print("Large purchase approved by system") else: print("Large purchase needs manual approval") # Build the chain small = SmallPurchaseHandler() medium = MediumPurchaseHandler() large = LargePurchaseHandler() small.set_next(medium).set_next(large) # Clean usage small.handle(50, regular_user) small.handle(500, vip_user) small.handle(5000, vip_user)
from abc import ABC, abstractmethod class PurchaseHandler(ABC): def __init__(self): self.next_handler = None def set_next(self, handler): self.next_handler = handler return handler @abstractmethod def handle(self, amount, user): if self.next_handler: return self.next_handler.handle(amount, user) class SmallPurchaseHandler(PurchaseHandler): def handle(self, amount, user): if amount < 100: discount = 0.2 if user.is_vip else 0.1 print(f"Small purchase: {discount*100}% discount") else: super().handle(amount, user) class MediumPurchaseHandler(PurchaseHandler): def handle(self, amount, user): if amount < 1000: discount = 0.3 if user.is_vip else 0.15 print(f"Medium purchase: {discount*100}% discount") else: super().handle(amount, user) class LargePurchaseHandler(PurchaseHandler): def handle(self, amount, user): if user.credit_score > 700: print("Large purchase approved by system") else: print("Large purchase needs manual approval") # Build the chain small = SmallPurchaseHandler() medium = MediumPurchaseHandler() large = LargePurchaseHandler() small.set_next(medium).set_next(large) # Clean usage small.handle(50, regular_user) small.handle(500, vip_user) small.handle(5000, vip_user)
from abc import ABC, abstractmethod class PurchaseHandler(ABC): def __init__(self): self.next_handler = None def set_next(self, handler): self.next_handler = handler return handler @abstractmethod def handle(self, amount, user): if self.next_handler: return self.next_handler.handle(amount, user) class SmallPurchaseHandler(PurchaseHandler): def handle(self, amount, user): if amount < 100: discount = 0.2 if user.is_vip else 0.1 print(f"Small purchase: {discount*100}% discount") else: super().handle(amount, user) class MediumPurchaseHandler(PurchaseHandler): def handle(self, amount, user): if amount < 1000: discount = 0.3 if user.is_vip else 0.15 print(f"Medium purchase: {discount*100}% discount") else: super().handle(amount, user) class LargePurchaseHandler(PurchaseHandler): def handle(self, amount, user): if user.credit_score > 700: print("Large purchase approved by system") else: print("Large purchase needs manual approval") # Build the chain small = SmallPurchaseHandler() medium = MediumPurchaseHandler() large = LargePurchaseHandler() small.set_next(medium).set_next(large) # Clean usage small.handle(50, regular_user) small.handle(500, vip_user) small.handle(5000, vip_user)
from abc import ABC, abstractmethod class PurchaseHandler(ABC): def __init__(self): self.next_handler = None def set_next(self, handler): self.next_handler = handler return handler @abstractmethod def handle(self, amount, user): if self.next_handler: return self.next_handler.handle(amount, user) class SmallPurchaseHandler(PurchaseHandler): def handle(self, amount, user): if amount < 100: discount = 0.2 if user.is_vip else 0.1 print(f"Small purchase: {discount*100}% discount") else: super().handle(amount, user) class MediumPurchaseHandler(PurchaseHandler): def handle(self, amount, user): if amount < 1000: discount = 0.3 if user.is_vip else 0.15 print(f"Medium purchase: {discount*100}% discount") else: super().handle(amount, user) class LargePurchaseHandler(PurchaseHandler): def handle(self, amount, user): if user.credit_score > 700: print("Large purchase approved by system") else: print("Large purchase needs manual approval") # Build the chain small = SmallPurchaseHandler() medium = MediumPurchaseHandler() large = LargePurchaseHandler() small.set_next(medium).set_next(large) # Clean usage small.handle(50, regular_user) small.handle(500, vip_user) small.handle(5000, vip_user)
const express = require('express'); const app = express(); // Authentication middleware function authenticate(req, res, next) { if (req.headers.authorization) { req.user = { id: 123, name: 'John' }; next(); // Pass to next handler } else { res.status(401).send('Unauthorized'); } } // Logging middleware function logger(req, res, next) { console.log(`${req.method} ${req.path} - ${new Date()}`); next(); // Pass to next handler } // Rate limiting middleware function rateLimit(req, res, next) { if (checkRateLimit(req.ip)) { next(); // Pass to next handler } else { res.status(429).send('Too many requests'); } } // Chain middlewares together app.use(logger); // First in chain app.use(authenticate); // Second in chain app.use(rateLimit); // Third in chain // Route handler (end of chain) app.get('/api/data', (req, res) => { res.json({ message: 'Success', user: req.user // Set by authenticate middleware }); }); // Request flows through: logger → authenticate → rateLimit → route handler
const express = require('express'); const app = express(); // Authentication middleware function authenticate(req, res, next) { if (req.headers.authorization) { req.user = { id: 123, name: 'John' }; next(); // Pass to next handler } else { res.status(401).send('Unauthorized'); } } // Logging middleware function logger(req, res, next) { console.log(`${req.method} ${req.path} - ${new Date()}`); next(); // Pass to next handler } // Rate limiting middleware function rateLimit(req, res, next) { if (checkRateLimit(req.ip)) { next(); // Pass to next handler } else { res.status(429).send('Too many requests'); } } // Chain middlewares together app.use(logger); // First in chain app.use(authenticate); // Second in chain app.use(rateLimit); // Third in chain // Route handler (end of chain) app.get('/api/data', (req, res) => { res.json({ message: 'Success', user: req.user // Set by authenticate middleware }); }); // Request flows through: logger → authenticate → rateLimit → route handler
const express = require('express'); const app = express(); // Authentication middleware function authenticate(req, res, next) { if (req.headers.authorization) { req.user = { id: 123, name: 'John' }; next(); // Pass to next handler } else { res.status(401).send('Unauthorized'); } } // Logging middleware function logger(req, res, next) { console.log(`${req.method} ${req.path} - ${new Date()}`); next(); // Pass to next handler } // Rate limiting middleware function rateLimit(req, res, next) { if (checkRateLimit(req.ip)) { next(); // Pass to next handler } else { res.status(429).send('Too many requests'); } } // Chain middlewares together app.use(logger); // First in chain app.use(authenticate); // Second in chain app.use(rateLimit); // Third in chain // Route handler (end of chain) app.get('/api/data', (req, res) => { res.json({ message: 'Success', user: req.user // Set by authenticate middleware }); }); // Request flows through: logger → authenticate → rateLimit → route handler
const express = require('express'); const app = express(); // Authentication middleware function authenticate(req, res, next) { if (req.headers.authorization) { req.user = { id: 123, name: 'John' }; next(); // Pass to next handler } else { res.status(401).send('Unauthorized'); } } // Logging middleware function logger(req, res, next) { console.log(`${req.method} ${req.path} - ${new Date()}`); next(); // Pass to next handler } // Rate limiting middleware function rateLimit(req, res, next) { if (checkRateLimit(req.ip)) { next(); // Pass to next handler } else { res.status(429).send('Too many requests'); } } // Chain middlewares together app.use(logger); // First in chain app.use(authenticate); // Second in chain app.use(rateLimit); // Third in chain // Route handler (end of chain) app.get('/api/data', (req, res) => { res.json({ message: 'Success', user: req.user // Set by authenticate middleware }); }); // Request flows through: logger → authenticate → rateLimit → route handler
const express = require('express'); const app = express(); // Authentication middleware function authenticate(req, res, next) { if (req.headers.authorization) { req.user = { id: 123, name: 'John' }; next(); // Pass to next handler } else { res.status(401).send('Unauthorized'); } } // Logging middleware function logger(req, res, next) { console.log(`${req.method} ${req.path} - ${new Date()}`); next(); // Pass to next handler } // Rate limiting middleware function rateLimit(req, res, next) { if (checkRateLimit(req.ip)) { next(); // Pass to next handler } else { res.status(429).send('Too many requests'); } } // Chain middlewares together app.use(logger); // First in chain app.use(authenticate); // Second in chain app.use(rateLimit); // Third in chain // Route handler (end of chain) app.get('/api/data', (req, res) => { res.json({ message: 'Success', user: req.user // Set by authenticate middleware }); }); // Request flows through: logger → authenticate → rateLimit → route handler
const express = require('express'); const app = express(); // Authentication middleware function authenticate(req, res, next) { if (req.headers.authorization) { req.user = { id: 123, name: 'John' }; next(); // Pass to next handler } else { res.status(401).send('Unauthorized'); } } // Logging middleware function logger(req, res, next) { console.log(`${req.method} ${req.path} - ${new Date()}`); next(); // Pass to next handler } // Rate limiting middleware function rateLimit(req, res, next) { if (checkRateLimit(req.ip)) { next(); // Pass to next handler } else { res.status(429).send('Too many requests'); } } // Chain middlewares together app.use(logger); // First in chain app.use(authenticate); // Second in chain app.use(rateLimit); // Third in chain // Route handler (end of chain) app.get('/api/data', (req, res) => { res.json({ message: 'Success', user: req.user // Set by authenticate middleware }); }); // Request flows through: logger → authenticate → rateLimit → route handler
Use the Command pattern when:
class TextEditor: def __init__(self): self.content = "" self.history = [] # For undo, but complex def type_text(self, text): self.history.append(('type', text)) self.content += text def delete_text(self, length): self.history.append(('delete', length)) self.content = self.content[:-length] def undo(self): # Complex logic to reverse different operations if self.history: action, value = self.history.pop() if action == 'type': self.content = self.content[:-len(value)] elif action == 'delete': # Oops, we lost the deleted text! pass # Problem: Undo is complex and buggy editor = TextEditor() editor.type_text("Hello") editor.delete_text(2) editor.undo() # Can't restore deleted text
class TextEditor: def __init__(self): self.content = "" self.history = [] # For undo, but complex def type_text(self, text): self.history.append(('type', text)) self.content += text def delete_text(self, length): self.history.append(('delete', length)) self.content = self.content[:-length] def undo(self): # Complex logic to reverse different operations if self.history: action, value = self.history.pop() if action == 'type': self.content = self.content[:-len(value)] elif action == 'delete': # Oops, we lost the deleted text! pass # Problem: Undo is complex and buggy editor = TextEditor() editor.type_text("Hello") editor.delete_text(2) editor.undo() # Can't restore deleted text
class TextEditor: def __init__(self): self.content = "" self.history = [] # For undo, but complex def type_text(self, text): self.history.append(('type', text)) self.content += text def delete_text(self, length): self.history.append(('delete', length)) self.content = self.content[:-length] def undo(self): # Complex logic to reverse different operations if self.history: action, value = self.history.pop() if action == 'type': self.content = self.content[:-len(value)] elif action == 'delete': # Oops, we lost the deleted text! pass # Problem: Undo is complex and buggy editor = TextEditor() editor.type_text("Hello") editor.delete_text(2) editor.undo() # Can't restore deleted text
# Command interface class Command: def execute(self): pass def undo(self): pass class TypeCommand(Command): def __init__(self, editor, text): self.editor = editor self.text = text def execute(self): self.editor.content += self.text def undo(self): length = len(self.text) self.editor.content = self.editor.content[:-length] class DeleteCommand(Command): def __init__(self, editor, length): self.editor = editor self.length = length self.deleted_text = "" def execute(self): self.deleted_text = self.editor.content[-self.length:] self.editor.content = self.editor.content[:-self.length] def undo(self): self.editor.content += self.deleted_text class TextEditor: def __init__(self): self.content = "" self.history = [] def execute_command(self, command): command.execute() self.history.append(command) def undo(self): if self.history: command = self.history.pop() command.undo() # Clean usage with perfect undo editor = TextEditor() editor.execute_command(TypeCommand(editor, "Hello")) editor.execute_command(DeleteCommand(editor, 2)) print(editor.content) # "Hel" editor.undo() print(editor.content) # "Hello"
# Command interface class Command: def execute(self): pass def undo(self): pass class TypeCommand(Command): def __init__(self, editor, text): self.editor = editor self.text = text def execute(self): self.editor.content += self.text def undo(self): length = len(self.text) self.editor.content = self.editor.content[:-length] class DeleteCommand(Command): def __init__(self, editor, length): self.editor = editor self.length = length self.deleted_text = "" def execute(self): self.deleted_text = self.editor.content[-self.length:] self.editor.content = self.editor.content[:-self.length] def undo(self): self.editor.content += self.deleted_text class TextEditor: def __init__(self): self.content = "" self.history = [] def execute_command(self, command): command.execute() self.history.append(command) def undo(self): if self.history: command = self.history.pop() command.undo() # Clean usage with perfect undo editor = TextEditor() editor.execute_command(TypeCommand(editor, "Hello")) editor.execute_command(DeleteCommand(editor, 2)) print(editor.content) # "Hel" editor.undo() print(editor.content) # "Hello"
# Command interface class Command: def execute(self): pass def undo(self): pass class TypeCommand(Command): def __init__(self, editor, text): self.editor = editor self.text = text def execute(self): self.editor.content += self.text def undo(self): length = len(self.text) self.editor.content = self.editor.content[:-length] class DeleteCommand(Command): def __init__(self, editor, length): self.editor = editor self.length = length self.deleted_text = "" def execute(self): self.deleted_text = self.editor.content[-self.length:] self.editor.content = self.editor.content[:-self.length] def undo(self): self.editor.content += self.deleted_text class TextEditor: def __init__(self): self.content = "" self.history = [] def execute_command(self, command): command.execute() self.history.append(command) def undo(self): if self.history: command = self.history.pop() command.undo() # Clean usage with perfect undo editor = TextEditor() editor.execute_command(TypeCommand(editor, "Hello")) editor.execute_command(DeleteCommand(editor, 2)) print(editor.content) # "Hel" editor.undo() print(editor.content) # "Hello"
# Command interface class Command: def execute(self): pass def undo(self): pass class TypeCommand(Command): def __init__(self, editor, text): self.editor = editor self.text = text def execute(self): self.editor.content += self.text def undo(self): length = len(self.text) self.editor.content = self.editor.content[:-length] class DeleteCommand(Command): def __init__(self, editor, length): self.editor = editor self.length = length self.deleted_text = "" def execute(self): self.deleted_text = self.editor.content[-self.length:] self.editor.content = self.editor.content[:-self.length] def undo(self): self.editor.content += self.deleted_text class TextEditor: def __init__(self): self.content = "" self.history = [] def execute_command(self, command): command.execute() self.history.append(command) def undo(self): if self.history: command = self.history.pop() command.undo() # Clean usage with perfect undo editor = TextEditor() editor.execute_command(TypeCommand(editor, "Hello")) editor.execute_command(DeleteCommand(editor, 2)) print(editor.content) # "Hel" editor.undo() print(editor.content) # "Hello"
# Command interface class Command: def execute(self): pass def undo(self): pass class TypeCommand(Command): def __init__(self, editor, text): self.editor = editor self.text = text def execute(self): self.editor.content += self.text def undo(self): length = len(self.text) self.editor.content = self.editor.content[:-length] class DeleteCommand(Command): def __init__(self, editor, length): self.editor = editor self.length = length self.deleted_text = "" def execute(self): self.deleted_text = self.editor.content[-self.length:] self.editor.content = self.editor.content[:-self.length] def undo(self): self.editor.content += self.deleted_text class TextEditor: def __init__(self): self.content = "" self.history = [] def execute_command(self, command): command.execute() self.history.append(command) def undo(self): if self.history: command = self.history.pop() command.undo() # Clean usage with perfect undo editor = TextEditor() editor.execute_command(TypeCommand(editor, "Hello")) editor.execute_command(DeleteCommand(editor, 2)) print(editor.content) # "Hel" editor.undo() print(editor.content) # "Hello"
# Command interface class Command: def execute(self): pass def undo(self): pass class TypeCommand(Command): def __init__(self, editor, text): self.editor = editor self.text = text def execute(self): self.editor.content += self.text def undo(self): length = len(self.text) self.editor.content = self.editor.content[:-length] class DeleteCommand(Command): def __init__(self, editor, length): self.editor = editor self.length = length self.deleted_text = "" def execute(self): self.deleted_text = self.editor.content[-self.length:] self.editor.content = self.editor.content[:-self.length] def undo(self): self.editor.content += self.deleted_text class TextEditor: def __init__(self): self.content = "" self.history = [] def execute_command(self, command): command.execute() self.history.append(command) def undo(self): if self.history: command = self.history.pop() command.undo() # Clean usage with perfect undo editor = TextEditor() editor.execute_command(TypeCommand(editor, "Hello")) editor.execute_command(DeleteCommand(editor, 2)) print(editor.content) # "Hel" editor.undo() print(editor.content) # "Hello"
// Redux implements Command pattern with actions // Actions are command objects const INCREMENT = 'INCREMENT'; const DECREMENT = 'DECREMENT'; const ADD_TODO = 'ADD_TODO'; // Action creators (command factories) function increment(amount = 1) { return { type: INCREMENT, payload: amount }; } function addTodo(text) { return { type: ADD_TODO, payload: { id: Date.now(), text, completed: false } }; }
// Redux implements Command pattern with actions // Actions are command objects const INCREMENT = 'INCREMENT'; const DECREMENT = 'DECREMENT'; const ADD_TODO = 'ADD_TODO'; // Action creators (command factories) function increment(amount = 1) { return { type: INCREMENT, payload: amount }; } function addTodo(text) { return { type: ADD_TODO, payload: { id: Date.now(), text, completed: false } }; }
// Redux implements Command pattern with actions // Actions are command objects const INCREMENT = 'INCREMENT'; const DECREMENT = 'DECREMENT'; const ADD_TODO = 'ADD_TODO'; // Action creators (command factories) function increment(amount = 1) { return { type: INCREMENT, payload: amount }; } function addTodo(text) { return { type: ADD_TODO, payload: { id: Date.now(), text, completed: false } }; }
// Reducer (command handler) function reducer(state = initialState, action) { switch (action.type) { case INCREMENT: return { ...state, count: state.count + action.payload }; case ADD_TODO: return { ...state, todos: [...state.todos, action.payload] }; default: return state; } } // Dispatch commands store.dispatch(increment(5)); store.dispatch(addTodo('Learn Command Pattern')); // Time travel debugging - Redux DevTools can replay commands // Undo/Redo by keeping history of dispatched actions
// Reducer (command handler) function reducer(state = initialState, action) { switch (action.type) { case INCREMENT: return { ...state, count: state.count + action.payload }; case ADD_TODO: return { ...state, todos: [...state.todos, action.payload] }; default: return state; } } // Dispatch commands store.dispatch(increment(5)); store.dispatch(addTodo('Learn Command Pattern')); // Time travel debugging - Redux DevTools can replay commands // Undo/Redo by keeping history of dispatched actions
// Reducer (command handler) function reducer(state = initialState, action) { switch (action.type) { case INCREMENT: return { ...state, count: state.count + action.payload }; case ADD_TODO: return { ...state, todos: [...state.todos, action.payload] }; default: return state; } } // Dispatch commands store.dispatch(increment(5)); store.dispatch(addTodo('Learn Command Pattern')); // Time travel debugging - Redux DevTools can replay commands // Undo/Redo by keeping history of dispatched actions
// Reducer (command handler) function reducer(state = initialState, action) { switch (action.type) { case INCREMENT: return { ...state, count: state.count + action.payload }; case ADD_TODO: return { ...state, todos: [...state.todos, action.payload] }; default: return state; } } // Dispatch commands store.dispatch(increment(5)); store.dispatch(addTodo('Learn Command Pattern')); // Time travel debugging - Redux DevTools can replay commands // Undo/Redo by keeping history of dispatched actions
Use the Strategy pattern when:
class ProductList: def __init__(self, products): self.products = products def sort_products(self, sort_type): # Big if/else block for different strategies if sort_type == "price": return sorted(self.products, key=lambda p: p.price) elif sort_type == "name": return sorted(self.products, key=lambda p: p.name) elif sort_type == "rating": return sorted(self.products, key=lambda p: p.rating, reverse=True) elif sort_type == "date": return sorted(self.products, key=lambda p: p.date, reverse=True) else: raise ValueError("Unknown sort type") # Problem: Adding new sort requires modifying the method products = ProductList(product_data) sorted_list = products.sort_products("price")
class ProductList: def __init__(self, products): self.products = products def sort_products(self, sort_type): # Big if/else block for different strategies if sort_type == "price": return sorted(self.products, key=lambda p: p.price) elif sort_type == "name": return sorted(self.products, key=lambda p: p.name) elif sort_type == "rating": return sorted(self.products, key=lambda p: p.rating, reverse=True) elif sort_type == "date": return sorted(self.products, key=lambda p: p.date, reverse=True) else: raise ValueError("Unknown sort type") # Problem: Adding new sort requires modifying the method products = ProductList(product_data) sorted_list = products.sort_products("price")
class ProductList: def __init__(self, products): self.products = products def sort_products(self, sort_type): # Big if/else block for different strategies if sort_type == "price": return sorted(self.products, key=lambda p: p.price) elif sort_type == "name": return sorted(self.products, key=lambda p: p.name) elif sort_type == "rating": return sorted(self.products, key=lambda p: p.rating, reverse=True) elif sort_type == "date": return sorted(self.products, key=lambda p: p.date, reverse=True) else: raise ValueError("Unknown sort type") # Problem: Adding new sort requires modifying the method products = ProductList(product_data) sorted_list = products.sort_products("price")
# Strategy interface class SortStrategy: def sort(self, products): raise NotImplementedError # Concrete strategies class PriceSortStrategy(SortStrategy): def sort(self, products): return sorted(products, key=lambda p: p.price) class NameSortStrategy(SortStrategy): def sort(self, products): return sorted(products, key=lambda p: p.name) class RatingSortStrategy(SortStrategy): def sort(self, products): return sorted(products, key=lambda p: p.rating, reverse=True) class PopularitySortStrategy(SortStrategy): def sort(self, products): # Complex sorting by views and purchases return sorted(products, key=lambda p: p.views * 0.3 + p.purchases * 0.7, reverse=True) class ProductList: def __init__(self, products): self.products = products self.sort_strategy = None def set_sort_strategy(self, strategy): self.sort_strategy = strategy def sort_products(self): if self.sort_strategy: return self.sort_strategy.sort(self.products) return self.products # Clean usage - easy to add new strategies products = ProductList(product_data) products.set_sort_strategy(PriceSortStrategy()) cheap_first = products.sort_products() products.set_sort_strategy(PopularitySortStrategy()) popular_first = products.sort_products()
# Strategy interface class SortStrategy: def sort(self, products): raise NotImplementedError # Concrete strategies class PriceSortStrategy(SortStrategy): def sort(self, products): return sorted(products, key=lambda p: p.price) class NameSortStrategy(SortStrategy): def sort(self, products): return sorted(products, key=lambda p: p.name) class RatingSortStrategy(SortStrategy): def sort(self, products): return sorted(products, key=lambda p: p.rating, reverse=True) class PopularitySortStrategy(SortStrategy): def sort(self, products): # Complex sorting by views and purchases return sorted(products, key=lambda p: p.views * 0.3 + p.purchases * 0.7, reverse=True) class ProductList: def __init__(self, products): self.products = products self.sort_strategy = None def set_sort_strategy(self, strategy): self.sort_strategy = strategy def sort_products(self): if self.sort_strategy: return self.sort_strategy.sort(self.products) return self.products # Clean usage - easy to add new strategies products = ProductList(product_data) products.set_sort_strategy(PriceSortStrategy()) cheap_first = products.sort_products() products.set_sort_strategy(PopularitySortStrategy()) popular_first = products.sort_products()
# Strategy interface class SortStrategy: def sort(self, products): raise NotImplementedError # Concrete strategies class PriceSortStrategy(SortStrategy): def sort(self, products): return sorted(products, key=lambda p: p.price) class NameSortStrategy(SortStrategy): def sort(self, products): return sorted(products, key=lambda p: p.name) class RatingSortStrategy(SortStrategy): def sort(self, products): return sorted(products, key=lambda p: p.rating, reverse=True) class PopularitySortStrategy(SortStrategy): def sort(self, products): # Complex sorting by views and purchases return sorted(products, key=lambda p: p.views * 0.3 + p.purchases * 0.7, reverse=True) class ProductList: def __init__(self, products): self.products = products self.sort_strategy = None def set_sort_strategy(self, strategy): self.sort_strategy = strategy def sort_products(self): if self.sort_strategy: return self.sort_strategy.sort(self.products) return self.products # Clean usage - easy to add new strategies products = ProductList(product_data) products.set_sort_strategy(PriceSortStrategy()) cheap_first = products.sort_products() products.set_sort_strategy(PopularitySortStrategy()) popular_first = products.sort_products()
# Strategy interface class SortStrategy: def sort(self, products): raise NotImplementedError # Concrete strategies class PriceSortStrategy(SortStrategy): def sort(self, products): return sorted(products, key=lambda p: p.price) class NameSortStrategy(SortStrategy): def sort(self, products): return sorted(products, key=lambda p: p.name) class RatingSortStrategy(SortStrategy): def sort(self, products): return sorted(products, key=lambda p: p.rating, reverse=True) class PopularitySortStrategy(SortStrategy): def sort(self, products): # Complex sorting by views and purchases return sorted(products, key=lambda p: p.views * 0.3 + p.purchases * 0.7, reverse=True) class ProductList: def __init__(self, products): self.products = products self.sort_strategy = None def set_sort_strategy(self, strategy): self.sort_strategy = strategy def sort_products(self): if self.sort_strategy: return self.sort_strategy.sort(self.products) return self.products # Clean usage - easy to add new strategies products = ProductList(product_data) products.set_sort_strategy(PriceSortStrategy()) cheap_first = products.sort_products() products.set_sort_strategy(PopularitySortStrategy()) popular_first = products.sort_products()
# Strategy interface class SortStrategy: def sort(self, products): raise NotImplementedError # Concrete strategies class PriceSortStrategy(SortStrategy): def sort(self, products): return sorted(products, key=lambda p: p.price) class NameSortStrategy(SortStrategy): def sort(self, products): return sorted(products, key=lambda p: p.name) class RatingSortStrategy(SortStrategy): def sort(self, products): return sorted(products, key=lambda p: p.rating, reverse=True) class PopularitySortStrategy(SortStrategy): def sort(self, products): # Complex sorting by views and purchases return sorted(products, key=lambda p: p.views * 0.3 + p.purchases * 0.7, reverse=True) class ProductList: def __init__(self, products): self.products = products self.sort_strategy = None def set_sort_strategy(self, strategy): self.sort_strategy = strategy def sort_products(self): if self.sort_strategy: return self.sort_strategy.sort(self.products) return self.products # Clean usage - easy to add new strategies products = ProductList(product_data) products.set_sort_strategy(PriceSortStrategy()) cheap_first = products.sort_products() products.set_sort_strategy(PopularitySortStrategy()) popular_first = products.sort_products()
Use the Template Method pattern when:
# Without Template Method - Duplicate code class CSVDataMiner: def mine_data(self, path): # Step 1: Open file file = open(path, 'r') # Step 2: Extract data raw_data = file.read() data = self.parse_csv(raw_data) # Step 3: Analyze result = self.analyze(data) # Step 4: Send report self.send_report(result) file.close() class JSONDataMiner: def mine_data(self, path): # Step 1: Open file (duplicate) file = open(path, 'r') # Step 2: Extract data (different) raw_data = file.read() data = self.parse_json(raw_data) # Step 3: Analyze (duplicate) result = self.analyze(data) # Step 4: Send report (duplicate) self.send_report(result) file.close()
# Without Template Method - Duplicate code class CSVDataMiner: def mine_data(self, path): # Step 1: Open file file = open(path, 'r') # Step 2: Extract data raw_data = file.read() data = self.parse_csv(raw_data) # Step 3: Analyze result = self.analyze(data) # Step 4: Send report self.send_report(result) file.close() class JSONDataMiner: def mine_data(self, path): # Step 1: Open file (duplicate) file = open(path, 'r') # Step 2: Extract data (different) raw_data = file.read() data = self.parse_json(raw_data) # Step 3: Analyze (duplicate) result = self.analyze(data) # Step 4: Send report (duplicate) self.send_report(result) file.close()
from abc import ABC, abstractmethod class DataMiner(ABC): def mine_data(self, path): """Template method defining the algorithm""" # Step 1: Open resource self.open_file(path) # Step 2: Extract - different per subclass raw_data = self.extract_data() # Step 3: Parse - different per subclass data = self.parse_data(raw_data) # Step 4: Analyze - common result = self.analyze(data) # Step 5: Send report - common self.send_report(result) # Step 6: Cleanup self.close_file() def open_file(self, path): self.file = open(path, 'r') def close_file(self): self.file.close() @abstractmethod def extract_data(self): pass @abstractmethod def parse_data(self, raw_data): pass def analyze(self, data): # Common analysis logic return {"count": len(data), "data": data} def send_report(self, result): print(f"Report: {result}") class CSVDataMiner(DataMiner): def extract_data(self): return self.file.read() def parse_data(self, raw_data): # CSV-specific parsing return raw_data.split(',') class JSONDataMiner(DataMiner): def extract_data(self): return self.file.read() def parse_data(self, raw_data): import json return json.loads(raw_data) # Usage - algorithm structure is enforced csv_miner = CSVDataMiner() csv_miner.mine_data("data.csv") json_miner = JSONDataMiner() json_miner.mine_data("data.json")
from abc import ABC, abstractmethod class DataMiner(ABC): def mine_data(self, path): """Template method defining the algorithm""" # Step 1: Open resource self.open_file(path) # Step 2: Extract - different per subclass raw_data = self.extract_data() # Step 3: Parse - different per subclass data = self.parse_data(raw_data) # Step 4: Analyze - common result = self.analyze(data) # Step 5: Send report - common self.send_report(result) # Step 6: Cleanup self.close_file() def open_file(self, path): self.file = open(path, 'r') def close_file(self): self.file.close() @abstractmethod def extract_data(self): pass @abstractmethod def parse_data(self, raw_data): pass def analyze(self, data): # Common analysis logic return {"count": len(data), "data": data} def send_report(self, result): print(f"Report: {result}") class CSVDataMiner(DataMiner): def extract_data(self): return self.file.read() def parse_data(self, raw_data): # CSV-specific parsing return raw_data.split(',') class JSONDataMiner(DataMiner): def extract_data(self): return self.file.read() def parse_data(self, raw_data): import json return json.loads(raw_data) # Usage - algorithm structure is enforced csv_miner = CSVDataMiner() csv_miner.mine_data("data.csv") json_miner = JSONDataMiner() json_miner.mine_data("data.json")
from abc import ABC, abstractmethod class DataMiner(ABC): def mine_data(self, path): """Template method defining the algorithm""" # Step 1: Open resource self.open_file(path) # Step 2: Extract - different per subclass raw_data = self.extract_data() # Step 3: Parse - different per subclass data = self.parse_data(raw_data) # Step 4: Analyze - common result = self.analyze(data) # Step 5: Send report - common self.send_report(result) # Step 6: Cleanup self.close_file() def open_file(self, path): self.file = open(path, 'r') def close_file(self): self.file.close() @abstractmethod def extract_data(self): pass @abstractmethod def parse_data(self, raw_data): pass def analyze(self, data): # Common analysis logic return {"count": len(data), "data": data} def send_report(self, result): print(f"Report: {result}") class CSVDataMiner(DataMiner): def extract_data(self): return self.file.read() def parse_data(self, raw_data): # CSV-specific parsing return raw_data.split(',') class JSONDataMiner(DataMiner): def extract_data(self): return self.file.read() def parse_data(self, raw_data): import json return json.loads(raw_data) # Usage - algorithm structure is enforced csv_miner = CSVDataMiner() csv_miner.mine_data("data.csv") json_miner = JSONDataMiner() json_miner.mine_data("data.json")
from abc import ABC, abstractmethod class DataMiner(ABC): def mine_data(self, path): """Template method defining the algorithm""" # Step 1: Open resource self.open_file(path) # Step 2: Extract - different per subclass raw_data = self.extract_data() # Step 3: Parse - different per subclass data = self.parse_data(raw_data) # Step 4: Analyze - common result = self.analyze(data) # Step 5: Send report - common self.send_report(result) # Step 6: Cleanup self.close_file() def open_file(self, path): self.file = open(path, 'r') def close_file(self): self.file.close() @abstractmethod def extract_data(self): pass @abstractmethod def parse_data(self, raw_data): pass def analyze(self, data): # Common analysis logic return {"count": len(data), "data": data} def send_report(self, result): print(f"Report: {result}") class CSVDataMiner(DataMiner): def extract_data(self): return self.file.read() def parse_data(self, raw_data): # CSV-specific parsing return raw_data.split(',') class JSONDataMiner(DataMiner): def extract_data(self): return self.file.read() def parse_data(self, raw_data): import json return json.loads(raw_data) # Usage - algorithm structure is enforced csv_miner = CSVDataMiner() csv_miner.mine_data("data.csv") json_miner = JSONDataMiner() json_miner.mine_data("data.json")
from abc import ABC, abstractmethod class DataMiner(ABC): def mine_data(self, path): """Template method defining the algorithm""" # Step 1: Open resource self.open_file(path) # Step 2: Extract - different per subclass raw_data = self.extract_data() # Step 3: Parse - different per subclass data = self.parse_data(raw_data) # Step 4: Analyze - common result = self.analyze(data) # Step 5: Send report - common self.send_report(result) # Step 6: Cleanup self.close_file() def open_file(self, path): self.file = open(path, 'r') def close_file(self): self.file.close() @abstractmethod def extract_data(self): pass @abstractmethod def parse_data(self, raw_data): pass def analyze(self, data): # Common analysis logic return {"count": len(data), "data": data} def send_report(self, result): print(f"Report: {result}") class CSVDataMiner(DataMiner): def extract_data(self): return self.file.read() def parse_data(self, raw_data): # CSV-specific parsing return raw_data.split(',') class JSONDataMiner(DataMiner): def extract_data(self): return self.file.read() def parse_data(self, raw_data): import json return json.loads(raw_data) # Usage - algorithm structure is enforced csv_miner = CSVDataMiner() csv_miner.mine_data("data.csv") json_miner = JSONDataMiner() json_miner.mine_data("data.json")
from abc import ABC, abstractmethod class DataMiner(ABC): def mine_data(self, path): """Template method defining the algorithm""" # Step 1: Open resource self.open_file(path) # Step 2: Extract - different per subclass raw_data = self.extract_data() # Step 3: Parse - different per subclass data = self.parse_data(raw_data) # Step 4: Analyze - common result = self.analyze(data) # Step 5: Send report - common self.send_report(result) # Step 6: Cleanup self.close_file() def open_file(self, path): self.file = open(path, 'r') def close_file(self): self.file.close() @abstractmethod def extract_data(self): pass @abstractmethod def parse_data(self, raw_data): pass def analyze(self, data): # Common analysis logic return {"count": len(data), "data": data} def send_report(self, result): print(f"Report: {result}") class CSVDataMiner(DataMiner): def extract_data(self): return self.file.read() def parse_data(self, raw_data): # CSV-specific parsing return raw_data.split(',') class JSONDataMiner(DataMiner): def extract_data(self): return self.file.read() def parse_data(self, raw_data): import json return json.loads(raw_data) # Usage - algorithm structure is enforced csv_miner = CSVDataMiner() csv_miner.mine_data("data.csv") json_miner = JSONDataMiner() json_miner.mine_data("data.json")
from abc import ABC, abstractmethod class DataMiner(ABC): def mine_data(self, path): """Template method defining the algorithm""" # Step 1: Open resource self.open_file(path) # Step 2: Extract - different per subclass raw_data = self.extract_data() # Step 3: Parse - different per subclass data = self.parse_data(raw_data) # Step 4: Analyze - common result = self.analyze(data) # Step 5: Send report - common self.send_report(result) # Step 6: Cleanup self.close_file() def open_file(self, path): self.file = open(path, 'r') def close_file(self): self.file.close() @abstractmethod def extract_data(self): pass @abstractmethod def parse_data(self, raw_data): pass def analyze(self, data): # Common analysis logic return {"count": len(data), "data": data} def send_report(self, result): print(f"Report: {result}") class CSVDataMiner(DataMiner): def extract_data(self): return self.file.read() def parse_data(self, raw_data): # CSV-specific parsing return raw_data.split(',') class JSONDataMiner(DataMiner): def extract_data(self): return self.file.read() def parse_data(self, raw_data): import json return json.loads(raw_data) # Usage - algorithm structure is enforced csv_miner = CSVDataMiner() csv_miner.mine_data("data.csv") json_miner = JSONDataMiner() json_miner.mine_data("data.json")
// React Class Components follow Template Method pattern // React defines the lifecycle template, you override specific methods class DataFetcher extends React.Component { constructor(props) { super(props); this.state = { data: null, loading: true }; } // Override: Called after component mounts componentDidMount() { fetch(this.props.url) .then(res => res.json()) .then(data => this.setState({ data, loading: false })); } // Override: Called before unmounting componentWillUnmount() { // Cleanup subscriptions, timers, etc. this.cancelRequests(); } // Override: Define how to render render() { if (this.state.loading) return <div>Loading...</div>; return <div>{this.state.data}</div>; } } // React calls these methods in specific order: // constructor → render → componentDidMount → render (on state change) → componentWillUnmount // You don't control the flow, just fill in the blanks
// React Class Components follow Template Method pattern // React defines the lifecycle template, you override specific methods class DataFetcher extends React.Component { constructor(props) { super(props); this.state = { data: null, loading: true }; } // Override: Called after component mounts componentDidMount() { fetch(this.props.url) .then(res => res.json()) .then(data => this.setState({ data, loading: false })); } // Override: Called before unmounting componentWillUnmount() { // Cleanup subscriptions, timers, etc. this.cancelRequests(); } // Override: Define how to render render() { if (this.state.loading) return <div>Loading...</div>; return <div>{this.state.data}</div>; } } // React calls these methods in specific order: // constructor → render → componentDidMount → render (on state change) → componentWillUnmount // You don't control the flow, just fill in the blanks
// React Class Components follow Template Method pattern // React defines the lifecycle template, you override specific methods class DataFetcher extends React.Component { constructor(props) { super(props); this.state = { data: null, loading: true }; } // Override: Called after component mounts componentDidMount() { fetch(this.props.url) .then(res => res.json()) .then(data => this.setState({ data, loading: false })); } // Override: Called before unmounting componentWillUnmount() { // Cleanup subscriptions, timers, etc. this.cancelRequests(); } // Override: Define how to render render() { if (this.state.loading) return <div>Loading...</div>; return <div>{this.state.data}</div>; } } // React calls these methods in specific order: // constructor → render → componentDidMount → render (on state change) → componentWillUnmount // You don't control the flow, just fill in the blanks
// React Class Components follow Template Method pattern // React defines the lifecycle template, you override specific methods class DataFetcher extends React.Component { constructor(props) { super(props); this.state = { data: null, loading: true }; } // Override: Called after component mounts componentDidMount() { fetch(this.props.url) .then(res => res.json()) .then(data => this.setState({ data, loading: false })); } // Override: Called before unmounting componentWillUnmount() { // Cleanup subscriptions, timers, etc. this.cancelRequests(); } // Override: Define how to render render() { if (this.state.loading) return <div>Loading...</div>; return <div>{this.state.data}</div>; } } // React calls these methods in specific order: // constructor → render → componentDidMount → render (on state change) → componentWillUnmount // You don't control the flow, just fill in the blanks
// React Class Components follow Template Method pattern // React defines the lifecycle template, you override specific methods class DataFetcher extends React.Component { constructor(props) { super(props); this.state = { data: null, loading: true }; } // Override: Called after component mounts componentDidMount() { fetch(this.props.url) .then(res => res.json()) .then(data => this.setState({ data, loading: false })); } // Override: Called before unmounting componentWillUnmount() { // Cleanup subscriptions, timers, etc. this.cancelRequests(); } // Override: Define how to render render() { if (this.state.loading) return <div>Loading...</div>; return <div>{this.state.data}</div>; } } // React calls these methods in specific order: // constructor → render → componentDidMount → render (on state change) → componentWillUnmount // You don't control the flow, just fill in the blanks
Use the Visitor pattern when:
# Without Visitor - Operations mixed with structure class File: def __init__(self, name, size): self.name = name self.size = size def get_size(self): return self.size def search(self, keyword): return keyword in self.name def compress(self): return f"Compressed {self.name}" class Directory: def __init__(self, name): self.name = name self.children = [] def get_size(self): return sum(child.get_size() for child in self.children) def search(self, keyword): results = [] if keyword in self.name: results.append(self.name) for child in self.children: results.extend(child.search(keyword)) return results def compress(self): compressed = [child.compress() for child in self.children] return f"Compressed dir {self.name}: {compressed}" # Problem: Adding new operations requires changing all classes
# Without Visitor - Operations mixed with structure class File: def __init__(self, name, size): self.name = name self.size = size def get_size(self): return self.size def search(self, keyword): return keyword in self.name def compress(self): return f"Compressed {self.name}" class Directory: def __init__(self, name): self.name = name self.children = [] def get_size(self): return sum(child.get_size() for child in self.children) def search(self, keyword): results = [] if keyword in self.name: results.append(self.name) for child in self.children: results.extend(child.search(keyword)) return results def compress(self): compressed = [child.compress() for child in self.children] return f"Compressed dir {self.name}: {compressed}" # Problem: Adding new operations requires changing all classes
# Without Visitor - Operations mixed with structure class File: def __init__(self, name, size): self.name = name self.size = size def get_size(self): return self.size def search(self, keyword): return keyword in self.name def compress(self): return f"Compressed {self.name}" class Directory: def __init__(self, name): self.name = name self.children = [] def get_size(self): return sum(child.get_size() for child in self.children) def search(self, keyword): results = [] if keyword in self.name: results.append(self.name) for child in self.children: results.extend(child.search(keyword)) return results def compress(self): compressed = [child.compress() for child in self.children] return f"Compressed dir {self.name}: {compressed}" # Problem: Adding new operations requires changing all classes
# Without Visitor - Operations mixed with structure class File: def __init__(self, name, size): self.name = name self.size = size def get_size(self): return self.size def search(self, keyword): return keyword in self.name def compress(self): return f"Compressed {self.name}" class Directory: def __init__(self, name): self.name = name self.children = [] def get_size(self): return sum(child.get_size() for child in self.children) def search(self, keyword): results = [] if keyword in self.name: results.append(self.name) for child in self.children: results.extend(child.search(keyword)) return results def compress(self): compressed = [child.compress() for child in self.children] return f"Compressed dir {self.name}: {compressed}" # Problem: Adding new operations requires changing all classes
from abc import ABC, abstractmethod # Element classes - structure class FileSystemElement(ABC): @abstractmethod def accept(self, visitor): pass class File(FileSystemElement): def __init__(self, name, size): self.name = name self.size = size def accept(self, visitor): return visitor.visit_file(self) class Directory(FileSystemElement): def __init__(self, name): self.name = name self.children = [] def accept(self, visitor): return visitor.visit_directory(self) # Visitor interface class Visitor(ABC): @abstractmethod def visit_file(self, file): pass @abstractmethod def visit_directory(self, directory): pass # Concrete visitors - operations class SizeCalculator(Visitor): def visit_file(self, file): return file.size def visit_directory(self, directory): total = 0 for child in directory.children: total += child.accept(self) return total class SearchVisitor(Visitor): def __init__(self, keyword): self.keyword = keyword self.results = [] def visit_file(self, file): if self.keyword in file.name: self.results.append(file.name) def visit_directory(self, directory): if self.keyword in directory.name: self.results.append(directory.name) for child in directory.children: child.accept(self) # Usage - Easy to add new operations root = Directory("root") root.children.append(File("readme.txt", 100)) src = Directory("src") src.children.append(File("main.py", 500)) root.children.append(src) # Calculate size size_calc = SizeCalculator() total_size = root.accept(size_calc) # 600 # Search searcher = SearchVisitor("main") root.accept(searcher) print(searcher.results) # ['main.py']
from abc import ABC, abstractmethod # Element classes - structure class FileSystemElement(ABC): @abstractmethod def accept(self, visitor): pass class File(FileSystemElement): def __init__(self, name, size): self.name = name self.size = size def accept(self, visitor): return visitor.visit_file(self) class Directory(FileSystemElement): def __init__(self, name): self.name = name self.children = [] def accept(self, visitor): return visitor.visit_directory(self) # Visitor interface class Visitor(ABC): @abstractmethod def visit_file(self, file): pass @abstractmethod def visit_directory(self, directory): pass # Concrete visitors - operations class SizeCalculator(Visitor): def visit_file(self, file): return file.size def visit_directory(self, directory): total = 0 for child in directory.children: total += child.accept(self) return total class SearchVisitor(Visitor): def __init__(self, keyword): self.keyword = keyword self.results = [] def visit_file(self, file): if self.keyword in file.name: self.results.append(file.name) def visit_directory(self, directory): if self.keyword in directory.name: self.results.append(directory.name) for child in directory.children: child.accept(self) # Usage - Easy to add new operations root = Directory("root") root.children.append(File("readme.txt", 100)) src = Directory("src") src.children.append(File("main.py", 500)) root.children.append(src) # Calculate size size_calc = SizeCalculator() total_size = root.accept(size_calc) # 600 # Search searcher = SearchVisitor("main") root.accept(searcher) print(searcher.results) # ['main.py']
from abc import ABC, abstractmethod # Element classes - structure class FileSystemElement(ABC): @abstractmethod def accept(self, visitor): pass class File(FileSystemElement): def __init__(self, name, size): self.name = name self.size = size def accept(self, visitor): return visitor.visit_file(self) class Directory(FileSystemElement): def __init__(self, name): self.name = name self.children = [] def accept(self, visitor): return visitor.visit_directory(self) # Visitor interface class Visitor(ABC): @abstractmethod def visit_file(self, file): pass @abstractmethod def visit_directory(self, directory): pass # Concrete visitors - operations class SizeCalculator(Visitor): def visit_file(self, file): return file.size def visit_directory(self, directory): total = 0 for child in directory.children: total += child.accept(self) return total class SearchVisitor(Visitor): def __init__(self, keyword): self.keyword = keyword self.results = [] def visit_file(self, file): if self.keyword in file.name: self.results.append(file.name) def visit_directory(self, directory): if self.keyword in directory.name: self.results.append(directory.name) for child in directory.children: child.accept(self) # Usage - Easy to add new operations root = Directory("root") root.children.append(File("readme.txt", 100)) src = Directory("src") src.children.append(File("main.py", 500)) root.children.append(src) # Calculate size size_calc = SizeCalculator() total_size = root.accept(size_calc) # 600 # Search searcher = SearchVisitor("main") root.accept(searcher) print(searcher.results) # ['main.py']
from abc import ABC, abstractmethod # Element classes - structure class FileSystemElement(ABC): @abstractmethod def accept(self, visitor): pass class File(FileSystemElement): def __init__(self, name, size): self.name = name self.size = size def accept(self, visitor): return visitor.visit_file(self) class Directory(FileSystemElement): def __init__(self, name): self.name = name self.children = [] def accept(self, visitor): return visitor.visit_directory(self) # Visitor interface class Visitor(ABC): @abstractmethod def visit_file(self, file): pass @abstractmethod def visit_directory(self, directory): pass # Concrete visitors - operations class SizeCalculator(Visitor): def visit_file(self, file): return file.size def visit_directory(self, directory): total = 0 for child in directory.children: total += child.accept(self) return total class SearchVisitor(Visitor): def __init__(self, keyword): self.keyword = keyword self.results = [] def visit_file(self, file): if self.keyword in file.name: self.results.append(file.name) def visit_directory(self, directory): if self.keyword in directory.name: self.results.append(directory.name) for child in directory.children: child.accept(self) # Usage - Easy to add new operations root = Directory("root") root.children.append(File("readme.txt", 100)) src = Directory("src") src.children.append(File("main.py", 500)) root.children.append(src) # Calculate size size_calc = SizeCalculator() total_size = root.accept(size_calc) # 600 # Search searcher = SearchVisitor("main") root.accept(searcher) print(searcher.results) # ['main.py']
from abc import ABC, abstractmethod # Element classes - structure class FileSystemElement(ABC): @abstractmethod def accept(self, visitor): pass class File(FileSystemElement): def __init__(self, name, size): self.name = name self.size = size def accept(self, visitor): return visitor.visit_file(self) class Directory(FileSystemElement): def __init__(self, name): self.name = name self.children = [] def accept(self, visitor): return visitor.visit_directory(self) # Visitor interface class Visitor(ABC): @abstractmethod def visit_file(self, file): pass @abstractmethod def visit_directory(self, directory): pass # Concrete visitors - operations class SizeCalculator(Visitor): def visit_file(self, file): return file.size def visit_directory(self, directory): total = 0 for child in directory.children: total += child.accept(self) return total class SearchVisitor(Visitor): def __init__(self, keyword): self.keyword = keyword self.results = [] def visit_file(self, file): if self.keyword in file.name: self.results.append(file.name) def visit_directory(self, directory): if self.keyword in directory.name: self.results.append(directory.name) for child in directory.children: child.accept(self) # Usage - Easy to add new operations root = Directory("root") root.children.append(File("readme.txt", 100)) src = Directory("src") src.children.append(File("main.py", 500)) root.children.append(src) # Calculate size size_calc = SizeCalculator() total_size = root.accept(size_calc) # 600 # Search searcher = SearchVisitor("main") root.accept(searcher) print(searcher.results) # ['main.py']
from abc import ABC, abstractmethod # Element classes - structure class FileSystemElement(ABC): @abstractmethod def accept(self, visitor): pass class File(FileSystemElement): def __init__(self, name, size): self.name = name self.size = size def accept(self, visitor): return visitor.visit_file(self) class Directory(FileSystemElement): def __init__(self, name): self.name = name self.children = [] def accept(self, visitor): return visitor.visit_directory(self) # Visitor interface class Visitor(ABC): @abstractmethod def visit_file(self, file): pass @abstractmethod def visit_directory(self, directory): pass # Concrete visitors - operations class SizeCalculator(Visitor): def visit_file(self, file): return file.size def visit_directory(self, directory): total = 0 for child in directory.children: total += child.accept(self) return total class SearchVisitor(Visitor): def __init__(self, keyword): self.keyword = keyword self.results = [] def visit_file(self, file): if self.keyword in file.name: self.results.append(file.name) def visit_directory(self, directory): if self.keyword in directory.name: self.results.append(directory.name) for child in directory.children: child.accept(self) # Usage - Easy to add new operations root = Directory("root") root.children.append(File("readme.txt", 100)) src = Directory("src") src.children.append(File("main.py", 500)) root.children.append(src) # Calculate size size_calc = SizeCalculator() total_size = root.accept(size_calc) # 600 # Search searcher = SearchVisitor("main") root.accept(searcher) print(searcher.results) # ['main.py']
from abc import ABC, abstractmethod # Element classes - structure class FileSystemElement(ABC): @abstractmethod def accept(self, visitor): pass class File(FileSystemElement): def __init__(self, name, size): self.name = name self.size = size def accept(self, visitor): return visitor.visit_file(self) class Directory(FileSystemElement): def __init__(self, name): self.name = name self.children = [] def accept(self, visitor): return visitor.visit_directory(self) # Visitor interface class Visitor(ABC): @abstractmethod def visit_file(self, file): pass @abstractmethod def visit_directory(self, directory): pass # Concrete visitors - operations class SizeCalculator(Visitor): def visit_file(self, file): return file.size def visit_directory(self, directory): total = 0 for child in directory.children: total += child.accept(self) return total class SearchVisitor(Visitor): def __init__(self, keyword): self.keyword = keyword self.results = [] def visit_file(self, file): if self.keyword in file.name: self.results.append(file.name) def visit_directory(self, directory): if self.keyword in directory.name: self.results.append(directory.name) for child in directory.children: child.accept(self) # Usage - Easy to add new operations root = Directory("root") root.children.append(File("readme.txt", 100)) src = Directory("src") src.children.append(File("main.py", 500)) root.children.append(src) # Calculate size size_calc = SizeCalculator() total_size = root.accept(size_calc) # 600 # Search searcher = SearchVisitor("main") root.accept(searcher) print(searcher.results) # ['main.py']
from abc import ABC, abstractmethod # Element classes - structure class FileSystemElement(ABC): @abstractmethod def accept(self, visitor): pass class File(FileSystemElement): def __init__(self, name, size): self.name = name self.size = size def accept(self, visitor): return visitor.visit_file(self) class Directory(FileSystemElement): def __init__(self, name): self.name = name self.children = [] def accept(self, visitor): return visitor.visit_directory(self) # Visitor interface class Visitor(ABC): @abstractmethod def visit_file(self, file): pass @abstractmethod def visit_directory(self, directory): pass # Concrete visitors - operations class SizeCalculator(Visitor): def visit_file(self, file): return file.size def visit_directory(self, directory): total = 0 for child in directory.children: total += child.accept(self) return total class SearchVisitor(Visitor): def __init__(self, keyword): self.keyword = keyword self.results = [] def visit_file(self, file): if self.keyword in file.name: self.results.append(file.name) def visit_directory(self, directory): if self.keyword in directory.name: self.results.append(directory.name) for child in directory.children: child.accept(self) # Usage - Easy to add new operations root = Directory("root") root.children.append(File("readme.txt", 100)) src = Directory("src") src.children.append(File("main.py", 500)) root.children.append(src) # Calculate size size_calc = SizeCalculator() total_size = root.accept(size_calc) # 600 # Search searcher = SearchVisitor("main") root.accept(searcher) print(searcher.results) # ['main.py']
from abc import ABC, abstractmethod # Element classes - structure class FileSystemElement(ABC): @abstractmethod def accept(self, visitor): pass class File(FileSystemElement): def __init__(self, name, size): self.name = name self.size = size def accept(self, visitor): return visitor.visit_file(self) class Directory(FileSystemElement): def __init__(self, name): self.name = name self.children = [] def accept(self, visitor): return visitor.visit_directory(self) # Visitor interface class Visitor(ABC): @abstractmethod def visit_file(self, file): pass @abstractmethod def visit_directory(self, directory): pass # Concrete visitors - operations class SizeCalculator(Visitor): def visit_file(self, file): return file.size def visit_directory(self, directory): total = 0 for child in directory.children: total += child.accept(self) return total class SearchVisitor(Visitor): def __init__(self, keyword): self.keyword = keyword self.results = [] def visit_file(self, file): if self.keyword in file.name: self.results.append(file.name) def visit_directory(self, directory): if self.keyword in directory.name: self.results.append(directory.name) for child in directory.children: child.accept(self) # Usage - Easy to add new operations root = Directory("root") root.children.append(File("readme.txt", 100)) src = Directory("src") src.children.append(File("main.py", 500)) root.children.append(src) # Calculate size size_calc = SizeCalculator() total_size = root.accept(size_calc) # 600 # Search searcher = SearchVisitor("main") root.accept(searcher) print(searcher.results) # ['main.py']
Use Dependency Injection when:
# Without DI - Hard dependencies class EmailService: def __init__(self): # Hardcoded SMTP configuration self.smtp_host = "smtp.gmail.com" self.smtp_port = 587 def send(self, to, subject, body): print(f"Sending email to {to}: {subject}") class UserService: def __init__(self): # Creates its own dependencies self.email_service = EmailService() self.database = PostgresDatabase() self.logger = FileLogger("/var/log/app.log") def register_user(self, email, name): # Save to database user = self.database.save({"email": email, "name": name}) # Send welcome email self.email_service.send(email, "Welcome!", f"Hi {name}") # Log the action self.logger.log(f"User registered: {email}") # Hard to test - can't mock dependencies # Hard to change - what if we want different email service? user_service = UserService()
# Without DI - Hard dependencies class EmailService: def __init__(self): # Hardcoded SMTP configuration self.smtp_host = "smtp.gmail.com" self.smtp_port = 587 def send(self, to, subject, body): print(f"Sending email to {to}: {subject}") class UserService: def __init__(self): # Creates its own dependencies self.email_service = EmailService() self.database = PostgresDatabase() self.logger = FileLogger("/var/log/app.log") def register_user(self, email, name): # Save to database user = self.database.save({"email": email, "name": name}) # Send welcome email self.email_service.send(email, "Welcome!", f"Hi {name}") # Log the action self.logger.log(f"User registered: {email}") # Hard to test - can't mock dependencies # Hard to change - what if we want different email service? user_service = UserService()
# Without DI - Hard dependencies class EmailService: def __init__(self): # Hardcoded SMTP configuration self.smtp_host = "smtp.gmail.com" self.smtp_port = 587 def send(self, to, subject, body): print(f"Sending email to {to}: {subject}") class UserService: def __init__(self): # Creates its own dependencies self.email_service = EmailService() self.database = PostgresDatabase() self.logger = FileLogger("/var/log/app.log") def register_user(self, email, name): # Save to database user = self.database.save({"email": email, "name": name}) # Send welcome email self.email_service.send(email, "Welcome!", f"Hi {name}") # Log the action self.logger.log(f"User registered: {email}") # Hard to test - can't mock dependencies # Hard to change - what if we want different email service? user_service = UserService()
# Without DI - Hard dependencies class EmailService: def __init__(self): # Hardcoded SMTP configuration self.smtp_host = "smtp.gmail.com" self.smtp_port = 587 def send(self, to, subject, body): print(f"Sending email to {to}: {subject}") class UserService: def __init__(self): # Creates its own dependencies self.email_service = EmailService() self.database = PostgresDatabase() self.logger = FileLogger("/var/log/app.log") def register_user(self, email, name): # Save to database user = self.database.save({"email": email, "name": name}) # Send welcome email self.email_service.send(email, "Welcome!", f"Hi {name}") # Log the action self.logger.log(f"User registered: {email}") # Hard to test - can't mock dependencies # Hard to change - what if we want different email service? user_service = UserService()
# With DI - Dependencies injected from abc import ABC, abstractmethod class EmailService(ABC): @abstractmethod def send(self, to, subject, body): pass class SMTPEmailService(EmailService): def __init__(self, host, port): self.host = host self.port = port def send(self, to, subject, body): print(f"SMTP: Sending to {to} via {self.host}") class UserService: def __init__(self, email_service, database, logger): # Dependencies injected from outside self.email_service = email_service self.database = database self.logger = logger def register_user(self, email, name): # Same logic, but with injected dependencies user = self.database.save({"email": email, "name": name}) self.email_service.send(email, "Welcome!", f"Hi {name}") self.logger.log(f"User registered: {email}") # Flexible configuration email = SMTPEmailService("smtp.company.com", 587) db = PostgresDatabase(connection_string) logger = CloudLogger(api_key) user_service = UserService(email, db, logger) # Easy to test with mocks test_email = MockEmailService() test_db = InMemoryDatabase() test_logger = ConsoleLogger() test_service = UserService(test_email, test_db, test_logger)
# With DI - Dependencies injected from abc import ABC, abstractmethod class EmailService(ABC): @abstractmethod def send(self, to, subject, body): pass class SMTPEmailService(EmailService): def __init__(self, host, port): self.host = host self.port = port def send(self, to, subject, body): print(f"SMTP: Sending to {to} via {self.host}") class UserService: def __init__(self, email_service, database, logger): # Dependencies injected from outside self.email_service = email_service self.database = database self.logger = logger def register_user(self, email, name): # Same logic, but with injected dependencies user = self.database.save({"email": email, "name": name}) self.email_service.send(email, "Welcome!", f"Hi {name}") self.logger.log(f"User registered: {email}") # Flexible configuration email = SMTPEmailService("smtp.company.com", 587) db = PostgresDatabase(connection_string) logger = CloudLogger(api_key) user_service = UserService(email, db, logger) # Easy to test with mocks test_email = MockEmailService() test_db = InMemoryDatabase() test_logger = ConsoleLogger() test_service = UserService(test_email, test_db, test_logger)
# With DI - Dependencies injected from abc import ABC, abstractmethod class EmailService(ABC): @abstractmethod def send(self, to, subject, body): pass class SMTPEmailService(EmailService): def __init__(self, host, port): self.host = host self.port = port def send(self, to, subject, body): print(f"SMTP: Sending to {to} via {self.host}") class UserService: def __init__(self, email_service, database, logger): # Dependencies injected from outside self.email_service = email_service self.database = database self.logger = logger def register_user(self, email, name): # Same logic, but with injected dependencies user = self.database.save({"email": email, "name": name}) self.email_service.send(email, "Welcome!", f"Hi {name}") self.logger.log(f"User registered: {email}") # Flexible configuration email = SMTPEmailService("smtp.company.com", 587) db = PostgresDatabase(connection_string) logger = CloudLogger(api_key) user_service = UserService(email, db, logger) # Easy to test with mocks test_email = MockEmailService() test_db = InMemoryDatabase() test_logger = ConsoleLogger() test_service = UserService(test_email, test_db, test_logger)
# With DI - Dependencies injected from abc import ABC, abstractmethod class EmailService(ABC): @abstractmethod def send(self, to, subject, body): pass class SMTPEmailService(EmailService): def __init__(self, host, port): self.host = host self.port = port def send(self, to, subject, body): print(f"SMTP: Sending to {to} via {self.host}") class UserService: def __init__(self, email_service, database, logger): # Dependencies injected from outside self.email_service = email_service self.database = database self.logger = logger def register_user(self, email, name): # Same logic, but with injected dependencies user = self.database.save({"email": email, "name": name}) self.email_service.send(email, "Welcome!", f"Hi {name}") self.logger.log(f"User registered: {email}") # Flexible configuration email = SMTPEmailService("smtp.company.com", 587) db = PostgresDatabase(connection_string) logger = CloudLogger(api_key) user_service = UserService(email, db, logger) # Easy to test with mocks test_email = MockEmailService() test_db = InMemoryDatabase() test_logger = ConsoleLogger() test_service = UserService(test_email, test_db, test_logger)
# With DI - Dependencies injected from abc import ABC, abstractmethod class EmailService(ABC): @abstractmethod def send(self, to, subject, body): pass class SMTPEmailService(EmailService): def __init__(self, host, port): self.host = host self.port = port def send(self, to, subject, body): print(f"SMTP: Sending to {to} via {self.host}") class UserService: def __init__(self, email_service, database, logger): # Dependencies injected from outside self.email_service = email_service self.database = database self.logger = logger def register_user(self, email, name): # Same logic, but with injected dependencies user = self.database.save({"email": email, "name": name}) self.email_service.send(email, "Welcome!", f"Hi {name}") self.logger.log(f"User registered: {email}") # Flexible configuration email = SMTPEmailService("smtp.company.com", 587) db = PostgresDatabase(connection_string) logger = CloudLogger(api_key) user_service = UserService(email, db, logger) # Easy to test with mocks test_email = MockEmailService() test_db = InMemoryDatabase() test_logger = ConsoleLogger() test_service = UserService(test_email, test_db, test_logger)
# With DI - Dependencies injected from abc import ABC, abstractmethod class EmailService(ABC): @abstractmethod def send(self, to, subject, body): pass class SMTPEmailService(EmailService): def __init__(self, host, port): self.host = host self.port = port def send(self, to, subject, body): print(f"SMTP: Sending to {to} via {self.host}") class UserService: def __init__(self, email_service, database, logger): # Dependencies injected from outside self.email_service = email_service self.database = database self.logger = logger def register_user(self, email, name): # Same logic, but with injected dependencies user = self.database.save({"email": email, "name": name}) self.email_service.send(email, "Welcome!", f"Hi {name}") self.logger.log(f"User registered: {email}") # Flexible configuration email = SMTPEmailService("smtp.company.com", 587) db = PostgresDatabase(connection_string) logger = CloudLogger(api_key) user_service = UserService(email, db, logger) # Easy to test with mocks test_email = MockEmailService() test_db = InMemoryDatabase() test_logger = ConsoleLogger() test_service = UserService(test_email, test_db, test_logger)
# With DI - Dependencies injected from abc import ABC, abstractmethod class EmailService(ABC): @abstractmethod def send(self, to, subject, body): pass class SMTPEmailService(EmailService): def __init__(self, host, port): self.host = host self.port = port def send(self, to, subject, body): print(f"SMTP: Sending to {to} via {self.host}") class UserService: def __init__(self, email_service, database, logger): # Dependencies injected from outside self.email_service = email_service self.database = database self.logger = logger def register_user(self, email, name): # Same logic, but with injected dependencies user = self.database.save({"email": email, "name": name}) self.email_service.send(email, "Welcome!", f"Hi {name}") self.logger.log(f"User registered: {email}") # Flexible configuration email = SMTPEmailService("smtp.company.com", 587) db = PostgresDatabase(connection_string) logger = CloudLogger(api_key) user_service = UserService(email, db, logger) # Easy to test with mocks test_email = MockEmailService() test_db = InMemoryDatabase() test_logger = ConsoleLogger() test_service = UserService(test_email, test_db, test_logger)
# With DI - Dependencies injected from abc import ABC, abstractmethod class EmailService(ABC): @abstractmethod def send(self, to, subject, body): pass class SMTPEmailService(EmailService): def __init__(self, host, port): self.host = host self.port = port def send(self, to, subject, body): print(f"SMTP: Sending to {to} via {self.host}") class UserService: def __init__(self, email_service, database, logger): # Dependencies injected from outside self.email_service = email_service self.database = database self.logger = logger def register_user(self, email, name): # Same logic, but with injected dependencies user = self.database.save({"email": email, "name": name}) self.email_service.send(email, "Welcome!", f"Hi {name}") self.logger.log(f"User registered: {email}") # Flexible configuration email = SMTPEmailService("smtp.company.com", 587) db = PostgresDatabase(connection_string) logger = CloudLogger(api_key) user_service = UserService(email, db, logger) # Easy to test with mocks test_email = MockEmailService() test_db = InMemoryDatabase() test_logger = ConsoleLogger() test_service = UserService(test_email, test_db, test_logger)
# With DI - Dependencies injected from abc import ABC, abstractmethod class EmailService(ABC): @abstractmethod def send(self, to, subject, body): pass class SMTPEmailService(EmailService): def __init__(self, host, port): self.host = host self.port = port def send(self, to, subject, body): print(f"SMTP: Sending to {to} via {self.host}") class UserService: def __init__(self, email_service, database, logger): # Dependencies injected from outside self.email_service = email_service self.database = database self.logger = logger def register_user(self, email, name): # Same logic, but with injected dependencies user = self.database.save({"email": email, "name": name}) self.email_service.send(email, "Welcome!", f"Hi {name}") self.logger.log(f"User registered: {email}") # Flexible configuration email = SMTPEmailService("smtp.company.com", 587) db = PostgresDatabase(connection_string) logger = CloudLogger(api_key) user_service = UserService(email, db, logger) # Easy to test with mocks test_email = MockEmailService() test_db = InMemoryDatabase() test_logger = ConsoleLogger() test_service = UserService(test_email, test_db, test_logger)
// Without DI - Hard dependencies function UserController() { // Controller creates its own dependencies this.httpService = new HttpService(); this.userService = new UserService(this.httpService); this.loggerService = new LoggerService('console'); this.loadUsers = function() { this.userService.getUsers().then(function(users) { // Handle users }); }; } // Problem: Hard to test, tightly coupled // Can't easily mock dependencies var controller = new UserController();
// Without DI - Hard dependencies function UserController() { // Controller creates its own dependencies this.httpService = new HttpService(); this.userService = new UserService(this.httpService); this.loggerService = new LoggerService('console'); this.loadUsers = function() { this.userService.getUsers().then(function(users) { // Handle users }); }; } // Problem: Hard to test, tightly coupled // Can't easily mock dependencies var controller = new UserController();
// Without DI - Hard dependencies function UserController() { // Controller creates its own dependencies this.httpService = new HttpService(); this.userService = new UserService(this.httpService); this.loggerService = new LoggerService('console'); this.loadUsers = function() { this.userService.getUsers().then(function(users) { // Handle users }); }; } // Problem: Hard to test, tightly coupled // Can't easily mock dependencies var controller = new UserController();
// With AngularJS DI - Dependencies injected function UserController($http, UserService, $log) { // Dependencies injected by Angular this.httpService = $http; this.userService = UserService; this.logger = $log; this.loadUsers = function() { this.userService.getUsers().then(function(users) { this.logger.info('Users loaded:', users.length); }); }; } // Register with Angular's DI container angular.module('myApp') .controller('UserController', [ '$http', 'UserService', '$log', UserController ]); // Easy to test with injected mocks // Angular handles dependency resolution
// With AngularJS DI - Dependencies injected function UserController($http, UserService, $log) { // Dependencies injected by Angular this.httpService = $http; this.userService = UserService; this.logger = $log; this.loadUsers = function() { this.userService.getUsers().then(function(users) { this.logger.info('Users loaded:', users.length); }); }; } // Register with Angular's DI container angular.module('myApp') .controller('UserController', [ '$http', 'UserService', '$log', UserController ]); // Easy to test with injected mocks // Angular handles dependency resolution
// With AngularJS DI - Dependencies injected function UserController($http, UserService, $log) { // Dependencies injected by Angular this.httpService = $http; this.userService = UserService; this.logger = $log; this.loadUsers = function() { this.userService.getUsers().then(function(users) { this.logger.info('Users loaded:', users.length); }); }; } // Register with Angular's DI container angular.module('myApp') .controller('UserController', [ '$http', 'UserService', '$log', UserController ]); // Easy to test with injected mocks // Angular handles dependency resolution
// With AngularJS DI - Dependencies injected function UserController($http, UserService, $log) { // Dependencies injected by Angular this.httpService = $http; this.userService = UserService; this.logger = $log; this.loadUsers = function() { this.userService.getUsers().then(function(users) { this.logger.info('Users loaded:', users.length); }); }; } // Register with Angular's DI container angular.module('myApp') .controller('UserController', [ '$http', 'UserService', '$log', UserController ]); // Easy to test with injected mocks // Angular handles dependency resolution
// Without DI - Hard dependencies public class OrderService { // Creates its own dependencies private PaymentService paymentService = new PaymentService(); private EmailService emailService = new EmailService(); private InventoryService inventoryService = new InventoryService(); public void processOrder(Order order) { inventoryService.reserveItems(order.getItems()); paymentService.processPayment(order.getPayment()); emailService.sendConfirmation(order.getCustomerEmail()); } } // Hard to test - can't mock services // Hard to change implementations OrderService service = new OrderService();
// Without DI - Hard dependencies public class OrderService { // Creates its own dependencies private PaymentService paymentService = new PaymentService(); private EmailService emailService = new EmailService(); private InventoryService inventoryService = new InventoryService(); public void processOrder(Order order) { inventoryService.reserveItems(order.getItems()); paymentService.processPayment(order.getPayment()); emailService.sendConfirmation(order.getCustomerEmail()); } } // Hard to test - can't mock services // Hard to change implementations OrderService service = new OrderService();
// Without DI - Hard dependencies public class OrderService { // Creates its own dependencies private PaymentService paymentService = new PaymentService(); private EmailService emailService = new EmailService(); private InventoryService inventoryService = new InventoryService(); public void processOrder(Order order) { inventoryService.reserveItems(order.getItems()); paymentService.processPayment(order.getPayment()); emailService.sendConfirmation(order.getCustomerEmail()); } } // Hard to test - can't mock services // Hard to change implementations OrderService service = new OrderService();
// With Spring DI - Dependencies injected @Service public class OrderService { @Autowired private PaymentService paymentService; @Autowired private EmailService emailService; @Autowired private InventoryService inventoryService; public void processOrder(Order order) { inventoryService.reserveItems(order.getItems()); paymentService.processPayment(order.getPayment()); emailService.sendConfirmation(order.getCustomerEmail()); } } // Spring configuration @Configuration public class AppConfig { @Bean public PaymentService paymentService() { return new StripePaymentService(); // Can easily switch implementations } @Bean public EmailService emailService() { return new SMTPEmailService(); } } // Easy testing with @MockBean @SpringBootTest class OrderServiceTest { @MockBean private PaymentService paymentService; @Test void testProcessOrder() { // Test with mocked dependencies } }
// With Spring DI - Dependencies injected @Service public class OrderService { @Autowired private PaymentService paymentService; @Autowired private EmailService emailService; @Autowired private InventoryService inventoryService; public void processOrder(Order order) { inventoryService.reserveItems(order.getItems()); paymentService.processPayment(order.getPayment()); emailService.sendConfirmation(order.getCustomerEmail()); } } // Spring configuration @Configuration public class AppConfig { @Bean public PaymentService paymentService() { return new StripePaymentService(); // Can easily switch implementations } @Bean public EmailService emailService() { return new SMTPEmailService(); } } // Easy testing with @MockBean @SpringBootTest class OrderServiceTest { @MockBean private PaymentService paymentService; @Test void testProcessOrder() { // Test with mocked dependencies } }
// With Spring DI - Dependencies injected @Service public class OrderService { @Autowired private PaymentService paymentService; @Autowired private EmailService emailService; @Autowired private InventoryService inventoryService; public void processOrder(Order order) { inventoryService.reserveItems(order.getItems()); paymentService.processPayment(order.getPayment()); emailService.sendConfirmation(order.getCustomerEmail()); } } // Spring configuration @Configuration public class AppConfig { @Bean public PaymentService paymentService() { return new StripePaymentService(); // Can easily switch implementations } @Bean public EmailService emailService() { return new SMTPEmailService(); } } // Easy testing with @MockBean @SpringBootTest class OrderServiceTest { @MockBean private PaymentService paymentService; @Test void testProcessOrder() { // Test with mocked dependencies } }
// With Spring DI - Dependencies injected @Service public class OrderService { @Autowired private PaymentService paymentService; @Autowired private EmailService emailService; @Autowired private InventoryService inventoryService; public void processOrder(Order order) { inventoryService.reserveItems(order.getItems()); paymentService.processPayment(order.getPayment()); emailService.sendConfirmation(order.getCustomerEmail()); } } // Spring configuration @Configuration public class AppConfig { @Bean public PaymentService paymentService() { return new StripePaymentService(); // Can easily switch implementations } @Bean public EmailService emailService() { return new SMTPEmailService(); } } // Easy testing with @MockBean @SpringBootTest class OrderServiceTest { @MockBean private PaymentService paymentService; @Test void testProcessOrder() { // Test with mocked dependencies } }
// Without DI - Hard dependencies import { HttpClient } from './http'; import { Logger } from './logger'; class UserService { // Creates its own dependencies private http = new HttpClient('https://api.example.com'); private logger = new Logger('console'); async getUser(id: string): Promise<User> { this.logger.info(`Fetching user ${id}`); return this.http.get(`/users/${id}`); } } // Problem: Hard to test, tightly coupled const userService = new UserService(); userService.getUser('123');
// Without DI - Hard dependencies import { HttpClient } from './http'; import { Logger } from './logger'; class UserService { // Creates its own dependencies private http = new HttpClient('https://api.example.com'); private logger = new Logger('console'); async getUser(id: string): Promise<User> { this.logger.info(`Fetching user ${id}`); return this.http.get(`/users/${id}`); } } // Problem: Hard to test, tightly coupled const userService = new UserService(); userService.getUser('123');
// With Effect TS - Dependencies as services import { Effect, Context } from 'effect'; // Define service interfaces interface HttpService { get: (url: string) => Effect.Effect<any, HttpError>; } interface LoggerService { info: (message: string) => Effect.Effect<void>; } // Create service tags const HttpService = Context.GenericTag<HttpService>('HttpService'); const LoggerService = Context.GenericTag<LoggerService>('LoggerService'); // Service implementation using Effect const getUser = (id: string) => Effect.gen(function* () { const http = yield* HttpService; const logger = yield* LoggerService; yield* logger.info(`Fetching user ${id}`); const user = yield* http.get(`/users/${id}`); return user; }); // Provide implementations const program = getUser('123').pipe( Effect.provideService(HttpService, { get: (url) => Effect.succeed({ data: 'mock' }) }), Effect.provideService(LoggerService, { info: (msg) => Effect.sync(() => console.log(msg)) }) ); // Easy to test with different implementations Effect.runSync(program);
// With Effect TS - Dependencies as services import { Effect, Context } from 'effect'; // Define service interfaces interface HttpService { get: (url: string) => Effect.Effect<any, HttpError>; } interface LoggerService { info: (message: string) => Effect.Effect<void>; } // Create service tags const HttpService = Context.GenericTag<HttpService>('HttpService'); const LoggerService = Context.GenericTag<LoggerService>('LoggerService'); // Service implementation using Effect const getUser = (id: string) => Effect.gen(function* () { const http = yield* HttpService; const logger = yield* LoggerService; yield* logger.info(`Fetching user ${id}`); const user = yield* http.get(`/users/${id}`); return user; }); // Provide implementations const program = getUser('123').pipe( Effect.provideService(HttpService, { get: (url) => Effect.succeed({ data: 'mock' }) }), Effect.provideService(LoggerService, { info: (msg) => Effect.sync(() => console.log(msg)) }) ); // Easy to test with different implementations Effect.runSync(program);
// With Effect TS - Dependencies as services import { Effect, Context } from 'effect'; // Define service interfaces interface HttpService { get: (url: string) => Effect.Effect<any, HttpError>; } interface LoggerService { info: (message: string) => Effect.Effect<void>; } // Create service tags const HttpService = Context.GenericTag<HttpService>('HttpService'); const LoggerService = Context.GenericTag<LoggerService>('LoggerService'); // Service implementation using Effect const getUser = (id: string) => Effect.gen(function* () { const http = yield* HttpService; const logger = yield* LoggerService; yield* logger.info(`Fetching user ${id}`); const user = yield* http.get(`/users/${id}`); return user; }); // Provide implementations const program = getUser('123').pipe( Effect.provideService(HttpService, { get: (url) => Effect.succeed({ data: 'mock' }) }), Effect.provideService(LoggerService, { info: (msg) => Effect.sync(() => console.log(msg)) }) ); // Easy to test with different implementations Effect.runSync(program);
// With Effect TS - Dependencies as services import { Effect, Context } from 'effect'; // Define service interfaces interface HttpService { get: (url: string) => Effect.Effect<any, HttpError>; } interface LoggerService { info: (message: string) => Effect.Effect<void>; } // Create service tags const HttpService = Context.GenericTag<HttpService>('HttpService'); const LoggerService = Context.GenericTag<LoggerService>('LoggerService'); // Service implementation using Effect const getUser = (id: string) => Effect.gen(function* () { const http = yield* HttpService; const logger = yield* LoggerService; yield* logger.info(`Fetching user ${id}`); const user = yield* http.get(`/users/${id}`); return user; }); // Provide implementations const program = getUser('123').pipe( Effect.provideService(HttpService, { get: (url) => Effect.succeed({ data: 'mock' }) }), Effect.provideService(LoggerService, { info: (msg) => Effect.sync(() => console.log(msg)) }) ); // Easy to test with different implementations Effect.runSync(program);
Use a DSL when:
# Without DSL - Verbose and error-prone query = { "collection": "users", "filters": [ {"field": "age", "operator": ">", "value": 18}, {"field": "status", "operator": "=", "value": "active"} ], "sort": {"field": "created_at", "order": "desc"}, "limit": 10 } result = database.execute(query) # SQL string concatenation - dangerous sql = "SELECT * FROM users WHERE age > 18 AND status = 'active' ORDER BY created_at DESC LIMIT 10"
# Without DSL - Verbose and error-prone query = { "collection": "users", "filters": [ {"field": "age", "operator": ">", "value": 18}, {"field": "status", "operator": "=", "value": "active"} ], "sort": {"field": "created_at", "order": "desc"}, "limit": 10 } result = database.execute(query) # SQL string concatenation - dangerous sql = "SELECT * FROM users WHERE age > 18 AND status = 'active' ORDER BY created_at DESC LIMIT 10"
# Without DSL - Verbose and error-prone query = { "collection": "users", "filters": [ {"field": "age", "operator": ">", "value": 18}, {"field": "status", "operator": "=", "value": "active"} ], "sort": {"field": "created_at", "order": "desc"}, "limit": 10 } result = database.execute(query) # SQL string concatenation - dangerous sql = "SELECT * FROM users WHERE age > 18 AND status = 'active' ORDER BY created_at DESC LIMIT 10"
# With DSL - Readable and safe class Query: def __init__(self, collection): self.collection = collection self.filters = [] self.sort_field = None self.limit_value = None def where(self, field, operator, value): self.filters.append((field, operator, value)) return self def order_by(self, field, direction="asc"): self.sort_field = (field, direction) return self def limit(self, count): self.limit_value = count return self def execute(self): # Convert to database query safely return database.execute(self._build_query()) # Fluent, readable syntax result = (Query("users") .where("age", ">", 18) .where("status", "=", "active") .order_by("created_at", "desc") .limit(10) .execute() ) # Can extend with more domain-specific methods class UserQuery(Query): def active_adults(self): return self.where("age", ">=", 18).where("status", "=", "active") def recent(self, days=7): cutoff = datetime.now() - timedelta(days=days) return self.where("created_at", ">", cutoff) # Even more readable recent_active_adults = (UserQuery("users") .active_adults() .recent(30) .limit(100) .execute() )
# With DSL - Readable and safe class Query: def __init__(self, collection): self.collection = collection self.filters = [] self.sort_field = None self.limit_value = None def where(self, field, operator, value): self.filters.append((field, operator, value)) return self def order_by(self, field, direction="asc"): self.sort_field = (field, direction) return self def limit(self, count): self.limit_value = count return self def execute(self): # Convert to database query safely return database.execute(self._build_query()) # Fluent, readable syntax result = (Query("users") .where("age", ">", 18) .where("status", "=", "active") .order_by("created_at", "desc") .limit(10) .execute() ) # Can extend with more domain-specific methods class UserQuery(Query): def active_adults(self): return self.where("age", ">=", 18).where("status", "=", "active") def recent(self, days=7): cutoff = datetime.now() - timedelta(days=days) return self.where("created_at", ">", cutoff) # Even more readable recent_active_adults = (UserQuery("users") .active_adults() .recent(30) .limit(100) .execute() )
# With DSL - Readable and safe class Query: def __init__(self, collection): self.collection = collection self.filters = [] self.sort_field = None self.limit_value = None def where(self, field, operator, value): self.filters.append((field, operator, value)) return self def order_by(self, field, direction="asc"): self.sort_field = (field, direction) return self def limit(self, count): self.limit_value = count return self def execute(self): # Convert to database query safely return database.execute(self._build_query()) # Fluent, readable syntax result = (Query("users") .where("age", ">", 18) .where("status", "=", "active") .order_by("created_at", "desc") .limit(10) .execute() ) # Can extend with more domain-specific methods class UserQuery(Query): def active_adults(self): return self.where("age", ">=", 18).where("status", "=", "active") def recent(self, days=7): cutoff = datetime.now() - timedelta(days=days) return self.where("created_at", ">", cutoff) # Even more readable recent_active_adults = (UserQuery("users") .active_adults() .recent(30) .limit(100) .execute() )
# With DSL - Readable and safe class Query: def __init__(self, collection): self.collection = collection self.filters = [] self.sort_field = None self.limit_value = None def where(self, field, operator, value): self.filters.append((field, operator, value)) return self def order_by(self, field, direction="asc"): self.sort_field = (field, direction) return self def limit(self, count): self.limit_value = count return self def execute(self): # Convert to database query safely return database.execute(self._build_query()) # Fluent, readable syntax result = (Query("users") .where("age", ">", 18) .where("status", "=", "active") .order_by("created_at", "desc") .limit(10) .execute() ) # Can extend with more domain-specific methods class UserQuery(Query): def active_adults(self): return self.where("age", ">=", 18).where("status", "=", "active") def recent(self, days=7): cutoff = datetime.now() - timedelta(days=days) return self.where("created_at", ">", cutoff) # Even more readable recent_active_adults = (UserQuery("users") .active_adults() .recent(30) .limit(100) .execute() )
# With DSL - Readable and safe class Query: def __init__(self, collection): self.collection = collection self.filters = [] self.sort_field = None self.limit_value = None def where(self, field, operator, value): self.filters.append((field, operator, value)) return self def order_by(self, field, direction="asc"): self.sort_field = (field, direction) return self def limit(self, count): self.limit_value = count return self def execute(self): # Convert to database query safely return database.execute(self._build_query()) # Fluent, readable syntax result = (Query("users") .where("age", ">", 18) .where("status", "=", "active") .order_by("created_at", "desc") .limit(10) .execute() ) # Can extend with more domain-specific methods class UserQuery(Query): def active_adults(self): return self.where("age", ">=", 18).where("status", "=", "active") def recent(self, days=7): cutoff = datetime.now() - timedelta(days=days) return self.where("created_at", ">", cutoff) # Even more readable recent_active_adults = (UserQuery("users") .active_adults() .recent(30) .limit(100) .execute() )
// LINQ is a DSL for querying data in C# var products = new List<Product> { new Product { Name = "Laptop", Price = 1000, Category = "Electronics" }, new Product { Name = "Book", Price = 20, Category = "Education" }, new Product { Name = "Phone", Price = 800, Category = "Electronics" } }; // Method syntax - fluent DSL var expensiveElectronics = products .Where(p => p.Category == "Electronics") .Where(p => p.Price > 500) .OrderBy(p => p.Price) .Select(p => new { p.Name, p.Price }) .ToList(); // Query syntax - SQL-like DSL var cheapItems = from p in products where p.Price < 100 orderby p.Name select p.Name; // LINQ provides a domain-specific language for data queries // that reads naturally and compiles to efficient code
// LINQ is a DSL for querying data in C# var products = new List<Product> { new Product { Name = "Laptop", Price = 1000, Category = "Electronics" }, new Product { Name = "Book", Price = 20, Category = "Education" }, new Product { Name = "Phone", Price = 800, Category = "Electronics" } }; // Method syntax - fluent DSL var expensiveElectronics = products .Where(p => p.Category == "Electronics") .Where(p => p.Price > 500) .OrderBy(p => p.Price) .Select(p => new { p.Name, p.Price }) .ToList(); // Query syntax - SQL-like DSL var cheapItems = from p in products where p.Price < 100 orderby p.Name select p.Name; // LINQ provides a domain-specific language for data queries // that reads naturally and compiles to efficient code
// LINQ is a DSL for querying data in C# var products = new List<Product> { new Product { Name = "Laptop", Price = 1000, Category = "Electronics" }, new Product { Name = "Book", Price = 20, Category = "Education" }, new Product { Name = "Phone", Price = 800, Category = "Electronics" } }; // Method syntax - fluent DSL var expensiveElectronics = products .Where(p => p.Category == "Electronics") .Where(p => p.Price > 500) .OrderBy(p => p.Price) .Select(p => new { p.Name, p.Price }) .ToList(); // Query syntax - SQL-like DSL var cheapItems = from p in products where p.Price < 100 orderby p.Name select p.Name; // LINQ provides a domain-specific language for data queries // that reads naturally and compiles to efficient code
;; LISP itself is a DSL for symbolic computation ;; Code as data - homoiconicity (defun factorial (n) (if (<= n 1) 1 (* n (factorial (- n 1))))) ;; Macros allow you to create your own DSL syntax (defmacro when (condition &body body) `(if ,condition (progn ,@body))) ;; Usage looks like built-in syntax (when (> x 10) (print "x is greater than 10") (setf y (* x 2))) ;; The macro expands to: ;; (if (> x 10) (progn (print "x is greater than 10") (setf y (* x 2)))) ;; LISP's uniform syntax makes it perfect for creating DSLs ;; Everything is a list, so you can manipulate code as data
;; LISP itself is a DSL for symbolic computation ;; Code as data - homoiconicity (defun factorial (n) (if (<= n 1) 1 (* n (factorial (- n 1))))) ;; Macros allow you to create your own DSL syntax (defmacro when (condition &body body) `(if ,condition (progn ,@body))) ;; Usage looks like built-in syntax (when (> x 10) (print "x is greater than 10") (setf y (* x 2))) ;; The macro expands to: ;; (if (> x 10) (progn (print "x is greater than 10") (setf y (* x 2)))) ;; LISP's uniform syntax makes it perfect for creating DSLs ;; Everything is a list, so you can manipulate code as data
;; LISP itself is a DSL for symbolic computation ;; Code as data - homoiconicity (defun factorial (n) (if (<= n 1) 1 (* n (factorial (- n 1))))) ;; Macros allow you to create your own DSL syntax (defmacro when (condition &body body) `(if ,condition (progn ,@body))) ;; Usage looks like built-in syntax (when (> x 10) (print "x is greater than 10") (setf y (* x 2))) ;; The macro expands to: ;; (if (> x 10) (progn (print "x is greater than 10") (setf y (* x 2)))) ;; LISP's uniform syntax makes it perfect for creating DSLs ;; Everything is a list, so you can manipulate code as data
;; LISP itself is a DSL for symbolic computation ;; Code as data - homoiconicity (defun factorial (n) (if (<= n 1) 1 (* n (factorial (- n 1))))) ;; Macros allow you to create your own DSL syntax (defmacro when (condition &body body) `(if ,condition (progn ,@body))) ;; Usage looks like built-in syntax (when (> x 10) (print "x is greater than 10") (setf y (* x 2))) ;; The macro expands to: ;; (if (> x 10) (progn (print "x is greater than 10") (setf y (* x 2)))) ;; LISP's uniform syntax makes it perfect for creating DSLs ;; Everything is a list, so you can manipulate code as data
// Rust macros allow creating domain-specific languages // Define a macro for creating HTML macro_rules! html { ( $tag:ident { $($content:tt)* } ) => { format!("<{}>{}</{}>", stringify!($tag), html!($($content)*), stringify!($tag)) }; ( $text:literal ) => { $text }; ( $($content:tt)* ) => { concat!($( html!($content) ),*) }; } // Usage looks like a mini HTML DSL let page = html! { html { body { h1 { "Welcome to Rust!" } p { "This is generated by a macro" } } } }; // The macro expands to proper HTML string generation // Rust macros are hygienic and type-safe at compile time // Perfect for creating readable DSLs while maintaining performance
// Rust macros allow creating domain-specific languages // Define a macro for creating HTML macro_rules! html { ( $tag:ident { $($content:tt)* } ) => { format!("<{}>{}</{}>", stringify!($tag), html!($($content)*), stringify!($tag)) }; ( $text:literal ) => { $text }; ( $($content:tt)* ) => { concat!($( html!($content) ),*) }; } // Usage looks like a mini HTML DSL let page = html! { html { body { h1 { "Welcome to Rust!" } p { "This is generated by a macro" } } } }; // The macro expands to proper HTML string generation // Rust macros are hygienic and type-safe at compile time // Perfect for creating readable DSLs while maintaining performance
// Rust macros allow creating domain-specific languages // Define a macro for creating HTML macro_rules! html { ( $tag:ident { $($content:tt)* } ) => { format!("<{}>{}</{}>", stringify!($tag), html!($($content)*), stringify!($tag)) }; ( $text:literal ) => { $text }; ( $($content:tt)* ) => { concat!($( html!($content) ),*) }; } // Usage looks like a mini HTML DSL let page = html! { html { body { h1 { "Welcome to Rust!" } p { "This is generated by a macro" } } } }; // The macro expands to proper HTML string generation // Rust macros are hygienic and type-safe at compile time // Perfect for creating readable DSLs while maintaining performance
Comments
Comments should explain WHY not, WHAT