Design Patterns: Strategy

Quick example of the Strategy pattern in Python

11.10.2015

Overview

This design pattern is called Strategy Pattern. It is used to define a family of algorithms, encapsulates each one, and make them interchangeable. Strategy design pattern lets an algorithm vary independently from clients that use it.

Code Example

For example, animals can “walk” in many different ways. Walking could be considered a strategy that is implemented by different types of animals:

from types import MethodType
    

class Animal(object):
        
    def __init__(self, *args, **kwargs):
        self.name = kwargs.pop('name', None) or 'Animal'
        if kwargs.get('walk', None):
            self.walk = MethodType(kwargs.pop('walk'), self)
    
    def walk(self):
        """
        Cause animal instance to walk
            
        Walking funcionallity is a strategy, and is intended to
        be implemented separately by different types of animals.
        """
        message = '{} should implement a walk method'.format(
            self.__class__.__name__)
        raise NotImplementedError(message)
    
    
# Here are some different walking algorithms that can be used with Animal
def snake_walk(self):
    print('I am slithering side to side because I am a {}.'.format(self.name))
    
def four_legged_animal_walk(self):
    print('I am using all four of my legs to walk because I am a(n) {}.'.format(
        self.name))
    
def two_legged_animal_walk(self):
    print('I am standing up on my two legs to walk because I am a {}.'.format(
        self.name))

Running this example would produce the following output:

generic_animal = Animal()
king_cobra = Animal(name='King Cobra', walk=snake_walk)
elephant = Animal(name='Elephant', walk=four_legged_animal_walk)
kangaroo = Animal(name='Kangaroo', walk=two_legged_animal_walk)
    
kangaroo.walk()
elephant.walk()
king_cobra.walk()
# This one will Raise a NotImplementedError to let the programmer
# know that the walk method is intended to be used as a strategy.
generic_animal.walk()
    
# OUTPUT:
#
# I am standing up on my two legs to walk because I am a Kangaroo.
# I am using all four of my legs to walk because I am a(n) Elephant.
# I am slithering side to side because I am a King Cobra.
# Traceback (most recent call last):
#   File "./strategy.py", line 56, in <
#     generic_animal.walk()
#   File "./strategy.py", line 30, in walk
#     raise NotImplementedError(message)
# NotImplementedError: Animal should implement a walk method 

Python-specific Details

Note that in languages like C++ or Java, this pattern is implemented using an abstract class or an interface to define a a strategy. In Python it makes more sense to just define some functions externally that can be added dynamically to a class using types.MethodType.