I understand that because Python has first-class functions, using the Strategy pattern is usually just a matter of passing a function as an argument, and doesn't require futzing with classes. But what if the "strategies" aren't single functions, but rather groups of related functions that logically should be selected as a set?
Like, here's a trivial and contrived example:
class HexFormatter(object):
"""Base class for strategies to format hexadecimal numbers."""
pass
class Motorola(HexFormatter):
"""Format Motorola style (e.g. $1234)"""
@staticmethod
def formatbyte(n):
return "$%02X" % n
@staticmethod
def formatword(n):
return "$%04X" % n
class Intel(HexFormatter):
"""Format Intel-style (e.g. 1234h)"""
@staticmethod
def formatbyte(n):
return "%02Xh" % n
@staticmethod
def formatword(n):
return "%04Xh" % n
The idea is you pick a strategy and you get the function for formatting bytes and the function for formatting words as a set, rather than needing to specify them individually. This example is akin to how you'd do it in a language like C++ (except the methods wouldn't be static because you can't have virtual static methods in C++) and it's not as if it doesn't work in Python. But it involves defining a bunch of "classes" that only have static methods and aren't meant to be instantiated, which seems un-Pythonic somehow.
Is there a more idiomatic way to do this in Python?
Strategy pattern follows the Open/close principle; a software application is open for extension but closed for modification. It means you can add any number of additional strategies without modifying the main class. It makes your code more flexible and easy to maintain.
Strategy is a behavioral design pattern that lets you define a family of algorithms, put each of them into a separate class, and make their objects interchangeable.
Use the Strategy pattern when you want to use different variants of an algorithm within an object and be able to switch from one algorithm to another during runtime. Use the Strategy when you have a lot of similar classes that only differ in the way they execute some behavior.
Strategy design pattern is one of the behavioral design pattern. Strategy pattern is used when we have multiple algorithm for a specific task and client decides the actual implementation to be used at runtime.
One option I've found quite nice when you have many functions in each strategy, although perhaps a bit too much for something as small as this, is to define each strategy in a separate module
motorola.py
def formatbyte(n):
return "$%02X" % n
def formatword(n):
return "$%04X" % n
intel.py
def formatbyte(n):
return "%02Xh" % n
def formatword(n):
return "%04Xh" % n
Then as modules are first-class objects in Python as you pointed out, you can simply pass them when using your strategy.
The other alternative is to just consider passing each element of a strategy as a plain function parameter to whatever they are being used for in client code based on your context. For instance instead of having
def some_func(obj):
obj.format_byte(...)
# other stuff
obj.format_word(...)
You could have
def some_func(format_byte, format_word):
format_byte(...)
#
format_word(...)
In any case it doesn't make sense to go the OOP route if you have no more than static methods for each strategy - no two instances of any type will be any different as there is no instance data.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With