Mastering the Strategy Pattern in Python: From Objects to Functions

Overview

The

allows developers to define a family of algorithms, encapsulate each one, and make them interchangeable. This technique decouples the behavior of a class from the class itself. By moving logic into discrete strategies, you can change how an application behaves at runtime without modifying its core structure. This is vital in environments where requirements shift rapidly, such as startups or evolving software products.

Mastering the Strategy Pattern in Python: From Objects to Functions
The Strategy Pattern: Write BETTER PYTHON CODE Part 3

Prerequisites

To follow this guide, you should have a firm grasp of

basics, including classes, methods, and list manipulation. Familiarity with
Object-Oriented Programming
principles—specifically inheritance and abstract base classes—is helpful for the classic implementation. For the functional approach, understanding first-class functions and basic type hinting is recommended.

Key Libraries & Tools

  • abc Library
    : A built-in Python module used to define blueprints for other classes, ensuring subclasses implement specific methods.
  • typing Module
    : Used for adding type hints to improve code clarity and catch errors before execution.
  • random Module
    : A standard library for shuffling or selecting elements, used here to demonstrate a random processing strategy.

Code Walkthrough

When code relies on massive if/else blocks to decide behavior, it suffers from weak cohesion. A single method becomes responsible for both high-level logic and every granular implementation detail.

The OOP Approach

We start by defining an abstract base class using the abc module. This enforces a strict contract for all strategies.

from abc import ABC, abstractmethod

class TicketOrderingStrategy(ABC):
    @abstractmethod
    def create_ordering(self, list): 
        pass

Subclasses like FIFOOrdering or RandomOrdering implement this method. The CustomerSupport class then accepts these strategy objects as arguments. This means adding a new behavior, like a "Black Hole" strategy that deletes tickets, requires zero changes to the original process_tickets method.

The Functional Approach

Python treats functions as first-class citizens, allowing a more concise implementation. Instead of classes, we use standalone functions.

def fifo_ordering(tickets):
    return tickets.copy()

def random_ordering(tickets):
    list_copy = tickets.copy()
    random.shuffle(list_copy)
    return list_copy

You pass the function name directly into the processor. This eliminates the boilerplate code of class definitions while maintaining the same dynamic flexibility.

Syntax Notes

In the functional version, specifying types for function parameters requires the Callable keyword from the typing module. The syntax Callable[[InputType], ReturnType] ensures that the strategy passed to a method matches the expected signature, providing a layer of safety similar to the abstract class approach.

Practical Examples

Beyond support tickets, the Strategy Pattern excels in:

  • Rendering Engines: Switching algorithms based on specific hardware (e.g., different VR headsets).
  • Payment Processing: Dynamically choosing between Stripe, PayPal, or Crypto at checkout.
  • Pathfinding: Swapping between A*, Dijkstra, or Breadth-First search in a navigation app.

Tips & Gotchas

Avoid over-engineering. If you only have two options that will never change, a simple if statement is fine. Use this pattern when you anticipate adding more behaviors or want to keep your main logic clean and testable. Always work with copies of data within your strategies to prevent unintended side effects on the original lists.

3 min read