Introduction
Behavioral design patterns are concerned with communication between objects, what goes on between objects, and how they operate together. These patterns help encapsulate processes and define communication mechanisms between objects.
Strategy Pattern
The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable, allowing the algorithm to vary independently from clients that use it.
from abc import ABC, abstractmethod
class PaymentStrategy(ABC):
@abstractmethod
def pay(self, amount):
pass
class CreditCardPayment(PaymentStrategy):
def __init__(self, card_number):
self.card_number = card_number
def pay(self, amount):
print(f"Paid ${amount} using Credit Card {self.card_number}")
class PayPalPayment(PaymentStrategy):
def __init__(self, email):
self.email = email
def pay(self, amount):
print(f"Paid ${amount} using PayPal account {self.email}")
class ShoppingCart:
def __init__(self):
self.items = []
self.payment_strategy = None
def add_item(self, item, price):
self.items.append((item, price))
def set_payment(self, strategy):
self.payment_strategy = strategy
def checkout(self):
total = sum(price for _, price in self.items)
self.payment_strategy.pay(total)
cart = ShoppingCart()
cart.add_item("Book", 20)
cart.add_item("Pen", 5)
cart.set_payment(CreditCardPayment("1234-5678-9012-3456"))
cart.checkout()
Command Pattern
The Command pattern encapsulates a request as an object, allowing parameterization of clients with different requests, queuing of requests, and support for undoable operations.
class Command(ABC):
@abstractmethod
def execute(self):
pass
@abstractmethod
def undo(self):
pass
class Light:
def on(self):
print("Light is ON")
def off(self):
print("Light is OFF")
class LightOnCommand(Command):
def __init__(self, light):
self.light = light
def execute(self):
self.light.on()
def undo(self):
self.light.off()
class RemoteControl:
def __init__(self):
self.history = []
def execute_command(self, command):
command.execute()
self.history.append(command)
def undo_last(self):
if self.history:
self.history.pop().undo()
light = Light()
remote = RemoteControl()
remote.execute_command(LightOnCommand(light))
remote.undo_last()
State Pattern
The State pattern allows an object to alter its behavior when its internal state changes, making the object appear to change its class.
class State(ABC):
@abstractmethod
def insert_coin(self, machine):
pass
@abstractmethod
def eject_coin(self, machine):
pass
@abstractmethod
def press_button(self, machine):
pass
class NoCoinState(State):
def insert_coin(self, machine):
print("Coin inserted")
machine.set_state(machine.has_coin_state)
def eject_coin(self, machine):
print("No coin to eject")
def press_button(self, machine):
print("Insert coin first")
class HasCoinState(State):
def insert_coin(self, machine):
print("Coin already inserted")
def eject_coin(self, machine):
print("Coin returned")
machine.set_state(machine.no_coin_state)
def press_button(self, machine):
print("Dispensing product")
machine.set_state(machine.no_coin_state)
class VendingMachine:
def __init__(self):
self.no_coin_state = NoCoinState()
self.has_coin_state = HasCoinState()
self.current_state = self.no_coin_state
def set_state(self, state):
self.current_state = state
def insert_coin(self):
self.current_state.insert_coin(self)
def press_button(self):
self.current_state.press_button(self)
machine = VendingMachine()
machine.insert_coin()
machine.press_button()
Practice Problems
- Implement different sorting strategies (QuickSort, MergeSort, BubbleSort) with a strategy pattern.
- Create a command pattern for a text editor with undo/redo functionality.
- Build a state machine for a vending machine with different states.
- Implement a payment strategy system with multiple payment methods.
- Create a document state workflow (Draft, Review, Published) using the state pattern.