Strategy pattern
Overview
The Strategy pattern lets you change how a piece of code works without changing the code itself. You do this by passing a function or an object (the "Strategy") that contains the rule you want to use. Imagine traveling to work. Your goal is always the same: reach the office. But your strategy changes. On a rainy day, you take a taxi. On a sunny day, you walk. You swap your strategy based on the situation. Here is a simple example in a shopping app. We want to calculate the final price after a discount: ```python # These are our strategies (rules) def no_discount(price): return price def student_discount(price): return price - 50 # This is our main code (context) def checkout(price, discount_strategy): # We call whichever strategy was passed in return discount_strategy(price) ``` Now we can swap the strategy when we check out: ```python # Using different strategies print(checkout(200, no_discount)) print(checkout(200, student_discount)) ``` Notice that the `checkout` function never changes. If we want to add a `festival_discount` tomorrow, we just write a new small function and pass it in. Let us practice. Here is a faded example. Fill in the blank to use the festival strategy. ```python def festival_discount(price): return price / 2 # Fill the blank to apply the festival discount final_price = checkout(500, ____) ``` The rule to remember: **The Strategy pattern lets you swap out the behavior of an object or function at runtime.** There is a sharp edge here. Every strategy must accept the exact same arguments. If `student_discount` suddenly needs a `student_id`, but `no_discount` does not, the `checkout` function will crash. All strategies must look exactly the same to the caller.
If you use many `if/elif` statements to handle different rules, your code becomes messy. The Strategy pattern splits each rule into its own small, clean function or class.
Where used: Payment processing (Credit Card vs UPI), Sorting lists (Alphabetical vs Date), Applying discounts
Why learn this
- You will know how to write code that is easy to extend without breaking existing logic.
- You will understand how Python's built-in `sort()` function uses this pattern.
Code walkthrough
def uppercase_rule(text):
return text.upper()
def lowercase_rule(text):
return text.lower()
def format_text(text, rule):
return rule(text)
result = format_text("Hello", uppercase_rule)
print(result)
Focus: return rule(text)
Aha moment
def add(a, b): return a + b
def multiply(a, b): return a * b
def calculate(x, y, math_strategy):
return math_strategy(x, y)
# Passing the built-in 'max' function!
print(calculate(10, 20, max))
Prediction: What does this print? Look closely at the strategy being passed.
Common guess: It crashes or prints an error.
Any function that takes the correct number of arguments can be a strategy. Here, Python's built-in `max` function takes two numbers, so it works perfectly as our strategy!
Common mistakes
- Different arguments: If one strategy requires two arguments but another requires only one, the code that calls them will break. All strategies must accept the exact same parameters.
- Too many simple strategies: If your rule is just one simple math operation that never changes, a Strategy might be too much extra code. Sometimes a simple `if` is enough.
Recall questions
- Explain the Strategy pattern in your own words, as if to a classmate.
- Why is the Strategy pattern better than writing a long list of `if/elif` statements?
- What happens if one of your discount strategies requires a `user_age` argument, but the other strategies do not?
Understanding checks
What does this code print?
Mountain road
The `navigate` function receives the `scenic_route` function. It calls it and returns the result. The behavior was swapped without changing `navigate`.
A developer writes a sorting function and passes a `highest_first` rule to it. Why is this an example of the Strategy pattern?
Because the rule for sorting (the strategy) is passed in from the outside, allowing the sort behavior to change without rewriting the sort function.
The core sorting logic stays the same, but the comparison rule is swapped out at runtime.
Practice tasks
Swap the strategy
Given this code, change the `print` line so that it uses the `upi_payment` strategy instead of `cash_payment`.
Continue learning
Previous: Class Creation and Instantiation
Previous: Defining functions and return values