Strategy Pattern
Part of the Object Oriented Programming section of Coddy's Python journey — lesson 48 of 64.
The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. You can switch algorithms at runtime without changing the client code.
Here are simple strategy classes for different payment methods:
class CreditCard:
def pay(self, amount):
return f"Paid ${amount} with Credit Card"
class PayPal:
def pay(self, amount):
return f"Paid ${amount} with PayPal"
class Bitcoin:
def pay(self, amount):
return f"Paid ${amount} with Bitcoin"Each strategy implements the same method (pay) but with different behavior.
Create a context class that uses strategies:
class ShoppingCart:
def __init__(self):
self.total = 0
self.payment_strategy = None
def add_item(self, price):
self.total += price
def set_payment_strategy(self, strategy):
self.payment_strategy = strategy
def checkout(self):
return self.payment_strategy.pay(self.total)The context class can switch between different payment strategies.
Use the strategy pattern:
cart = ShoppingCart()
cart.add_item(50)
cart.add_item(30)
# Use credit card strategy
cart.set_payment_strategy(CreditCard())
print(cart.checkout())
# Switch to PayPal strategy
cart.set_payment_strategy(PayPal())
print(cart.checkout())Create another example with sorting strategies:
class BubbleSort:
def sort(self, data):
return f"Bubble sorted: {sorted(data)}"
class QuickSort:
def sort(self, data):
return f"Quick sorted: {sorted(data)}"
class Sorter:
def __init__(self, strategy):
self.strategy = strategy
def set_strategy(self, strategy):
self.strategy = strategy
def sort_data(self, data):
return self.strategy.sort(data)
# Use different sorting strategies
numbers = [3, 1, 4, 1, 5]
sorter = Sorter(BubbleSort())
print(sorter.sort_data(numbers))
sorter.set_strategy(QuickSort())
print(sorter.sort_data(numbers))Output:
Paid $80 with Credit Card
Paid $80 with PayPal
Bubble sorted: [1, 1, 3, 4, 5]
Quick sorted: [1, 1, 3, 4, 5]Key Point: The Strategy Pattern lets you swap algorithms at runtime. Define different strategies with the same interface, then let the context class choose which one to use. This makes your code flexible and easy to extend with new algorithms without changing existing code.
Challenge
MediumImplement a new BitcoinPayment strategy for our shopping system.
Your task is to:
- Create a BitcoinPayment class that implements PaymentStrategy
- It should accept a wallet_address in the constructor
- The pay method should print exactly:
Paying $X using Bitcoin wallet: Ywhere X is the amount and Y is the wallet address - The pay method should return True after printing
- Write a main function that:
- Creates a shopping cart
- Adds a laptop costing $1200 and headphones costing $100 to the cart
- Creates a BitcoinPayment with the wallet address "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
- Sets this payment strategy on the cart
- Calls the checkout method
The starter code already includes if __name__ == "__main__": at the bottom — you do not need to add it yourself. This is a Python convention that ensures your main() function only runs when the file is executed directly (not when it is imported by another module). Simply define your main() function and the provided block will call it automatically.
Follow the same structure as shown in the lesson examples.
Cheat sheet
The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. You can switch algorithms at runtime without changing the client code.
Create strategy classes with the same interface:
class CreditCard:
def pay(self, amount):
return f"Paid ${amount} with Credit Card"
class PayPal:
def pay(self, amount):
return f"Paid ${amount} with PayPal"
class Bitcoin:
def pay(self, amount):
return f"Paid ${amount} with Bitcoin"Create a context class that uses strategies:
class ShoppingCart:
def __init__(self):
self.total = 0
self.payment_strategy = None
def add_item(self, price):
self.total += price
def set_payment_strategy(self, strategy):
self.payment_strategy = strategy
def checkout(self):
return self.payment_strategy.pay(self.total)Use the strategy pattern:
cart = ShoppingCart()
cart.add_item(50)
cart.add_item(30)
# Use credit card strategy
cart.set_payment_strategy(CreditCard())
print(cart.checkout())
# Switch to PayPal strategy
cart.set_payment_strategy(PayPal())
print(cart.checkout())Another example with sorting strategies:
class BubbleSort:
def sort(self, data):
return f"Bubble sorted: {sorted(data)}"
class QuickSort:
def sort(self, data):
return f"Quick sorted: {sorted(data)}"
class Sorter:
def __init__(self, strategy):
self.strategy = strategy
def set_strategy(self, strategy):
self.strategy = strategy
def sort_data(self, data):
return self.strategy.sort(data)Try it yourself
from abc import ABC, abstractmethod
class PaymentStrategy(ABC):
@abstractmethod
def pay(self, amount):
pass
class CreditCardPayment(PaymentStrategy):
def __init__(self, card_number, expiry_date, cvv):
self.card_number = card_number
self.expiry_date = expiry_date
self.cvv = cvv
def pay(self, amount):
print(f"Paying ${amount} using Credit Card: {self.card_number}")
return True
class PayPalPayment(PaymentStrategy):
def __init__(self, email, password):
self.email = email
self.password = password
def pay(self, amount):
print(f"Paying ${amount} using PayPal account: {self.email}")
return True
class ShoppingCart:
def __init__(self):
self.items = []
self.payment_strategy = None
def add_item(self, item, price):
self.items.append({"item": item, "price": price})
def set_payment_strategy(self, payment_strategy):
self.payment_strategy = payment_strategy
def checkout(self):
total = sum(item["price"] for item in self.items)
if self.payment_strategy:
return self.payment_strategy.pay(total)
else:
raise ValueError("No payment strategy set")
# Create your BitcoinPayment strategy class here
if __name__ == "__main__":
main()This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesIntroduction to OOPClasses vs ObjectsThe self ParameterMethodsAttributesConstructor Method (__init__)Recap - Simple Calculator4Inheritance
Basic InheritanceThe super() FunctionMethod OverridingMultiple InheritanceMethod Resolution OrderRecap - Employee Hierarchy7Special Methods
Magic Methods IntroductionOperator OverloadingContainer Magic MethodsRecap - Custom List10Design Patterns Part 1
Intro to design patternSingleton PatternFactory PatternObserver PatternStrategy Pattern2Decorators
Introduction to DecoratorsProperty DecoratorStatic Method DecoratorClass Method Decorator5Polymorphism
Method Overriding RevisitedDuck TypingAbstract Classes and MethodsInterface DesignRecap - Shape Calculator8Advanced OOP Concepts
Composition vs InheritanceMixinsStatic and Class MethodsClass DecoratorsContext Managers3Class Properties
Instance vs Class VariablesProperty DecoratorsPrivate AttributesRecap - Bank Account Manager6Encapsulation
Public, Protected, Private MemAccess ModifiersInformation HidingProperty Decorators AdvancedRecap - Student Records System12Project: Library Management
Project OverviewBook and User Classes