How to write Strategy Pattern in Python differently than example in Wikipedia?
The example in Python is not so different of the others. To mock the PHP script:
class StrategyExample:
def __init__(self, func=None):
if func:
self.execute = func
def execute(self):
print("Original execution")
def executeReplacement1():
print("Strategy 1")
def executeReplacement2():
print("Strategy 2")
if __name__ == "__main__":
strat0 = StrategyExample()
strat1 = StrategyExample(executeReplacement1)
strat2 = StrategyExample(executeReplacement2)
strat0.execute()
strat1.execute()
strat2.execute()
Output:
Original execution
Strategy 1
Strategy 2
The main differences are:
- You don't need to write any other class or implement any interface.
- Instead you can pass a function reference that will be bound to the method you want.
- The functions can still be used separately, and the original object can have a default behavior if you want to (the
if func == None
pattern can be used for that). - Indeed, it's clean short and elegant as usual with Python. But you lose information; with no explicit interface, the programmer is assumed as an adult to know what they are doing.
Note that there are 3 ways to dynamically add a method in Python:
The way I've shown you. But the method will be static, it won't get the "self" argument passed.
Using the class name:
StrategyExample.execute = func
Here, all the instance will get func
as the execute
method, and will get self
passed as an argument.
Binding to an instance only (using the
types
module):strat0.execute = types.MethodType(executeReplacement1, strat0)
or with Python 2, the class of the instance being changed is also required:
strat0.execute = types.MethodType(executeReplacement1, strat0,
StrategyExample)
This will bind the new method to strat0
, and only strat0
, like with the first example. But start0.execute()
will get self
passed as an argument.
If you need to use a reference to the current instance in the function, then you would combine the first and the last method. If you do not:
class StrategyExample:
def __init__(self, func=None):
self.name = "Strategy Example 0"
if func:
self.execute = func
def execute(self):
print(self.name)
def executeReplacement1():
print(self.name + " from execute 1")
def executeReplacement2():
print(self.name + " from execute 2")
if __name__ == "__main__":
strat0 = StrategyExample()
strat1 = StrategyExample(executeReplacement1)
strat1.name = "Strategy Example 1"
strat2 = StrategyExample(executeReplacement2)
strat2.name = "Strategy Example 2"
strat0.execute()
strat1.execute()
strat2.execute()
You will get:
Traceback (most recent call last):
File "test.py", line 28, in <module>
strat1.execute()
File "test.py", line 13, in executeReplacement1
print self.name + " from execute 1"
NameError: global name 'self' is not defined
So the proper code would be:
import sys
import types
if sys.version_info[0] > 2: # Python 3+
create_bound_method = types.MethodType
else:
def create_bound_method(func, obj):
return types.MethodType(func, obj, obj.__class__)
class StrategyExample:
def __init__(self, func=None):
self.name = "Strategy Example 0"
if func:
self.execute = create_bound_method(func, self)
def execute(self):
print(self.name)
def executeReplacement1(self):
print(self.name + " from execute 1")
def executeReplacement2(self):
print(self.name + " from execute 2")
if __name__ == "__main__":
strat0 = StrategyExample()
strat1 = StrategyExample(executeReplacement1)
strat1.name = "Strategy Example 1"
strat2 = StrategyExample(executeReplacement2)
strat2.name = "Strategy Example 2"
strat0.execute()
strat1.execute()
strat2.execute()
This will output the expected result:
Strategy Example 0
Strategy Example 1 from execute 1
Strategy Example 2 from execute 2
Of course, in the case the functions cannot be used stand alone anymore, but can still be bound to any other instance of any object, without any interface limitation.
Strategy pattern in python
If you really wanted to go classes all the way and enforce your base class usage create an ABC: abstract base class / method and some implementations of it:
Attributation: used Alex Vasses answer here for lookup purposes
from abc import ABC, abstractmethod
class BaseLogger(ABC):
""" Base class specifying one abstractmethod log - tbd by subclasses."""
@abstractmethod
def log(self, message):
pass
class ConsoleLogger(BaseLogger):
""" Console logger implementation."""
def log(self, message):
print(message)
class FileLogger(BaseLogger):
""" Appending FileLogger (date based file names) implementation."""
def __init__(self):
import datetime
self.fn = datetime.datetime.now().strftime("%Y_%m_%d.log")
def log(self,message):
with open(self.fn,"a") as f:
f.write(f"file: {message}\n")
class NotALogger():
""" Not a logger implementation."""
pass
Then use them:
# import typing # for other type things
class DoIt:
def __init__(self, logger: BaseLogger):
# enforce usage of BaseLogger implementation
if isinstance(logger, BaseLogger):
self.logger = logger
else:
raise ValueError("logger needs to inherit from " + BaseLogger.__name__)
def log(self, message):
# use the assigned logger
self.logger.log(message)
# provide different logger classes
d1 = DoIt(ConsoleLogger())
d2 = DoIt(FileLogger())
for k in range(5):
d1.log(str(k))
d2.log(str(k))
with open(d2.logger.fn) as f:
print(f.read())
try:
d3 = DoIt( NotALogger())
except Exception as e:
print(e)
Output:
0
1
2
3
4
file: 0
file: 1
file: 2
file: 3
file: 4
logger needs to inherit from BaseLogger
As a sidenote: python already has quite sophisticated abilities to log. Look into Logging if that is the sole purpose of your inquiry.
How to implement Strategy Pattern with Base class dependencies?
The function should be defined to take a DB
instance as an argument, then DB.execute
can pass self
when calling it.
class DB():
def __init__(self, league, season, func=None):
self.db_user = os.environ.get('DB_user')
self.db_pass = os.environ.get('DB_pass')
self.MONGODB_URL = f'mongodb+srv://{db_user}:{db_pass}@database-mbqxj.mongodb.net/test?retryWrites=true&w=majority'
self.client = MongoClient(MONGODB_URL)
self.DATABASE = client["Database"]
self.league = league
self.season = season
self.pool = multiprocessing.cpu_count()
self.func = func
def execute(self):
if self.func is not None:
return self.func(self)
def executeReplacement1(db):
print("Push some data to DATABASE")
def executeReplacement2(db):
print("Push some other data to DATABASE")
What is the difference between the bridge pattern and the strategy pattern?
Semantics. From wikipedia:
The UML class diagram for the Strategy
pattern is the same as the diagram for
the Bridge pattern. However, these two
design patterns aren't the same in
their intent. While the Strategy
pattern is meant for behavior, the
Bridge pattern is meant for structure.The coupling between the context and
the strategies is tighter than the
coupling between the abstraction and
the implementation in the Bridge
pattern.
As I understand it, you're using the strategy pattern when you're abstracting behavior that could be provided from an external source (eg. config could specify to load some plugin assembly), and you're using the bridge pattern when you use the same constructs to make your code a bit neater. The actual code will look very similar - you're just applying the patterns for slightly different reasons.
What is the difference between Strategy pattern and Dependency Injection?
DI and Strategy work in the same way, but Strategy is used for more fine-grained and short-lived dependencies.
When an object is configured with a "fixed" Strategy, for example when the object is constructed, the distinction between Strategy and DI blurs. But in a DI scenario it is more unusual that the dependencies of objects change during their lifetimes, while this is not uncommon with Strategy.
Also, you can pass strategies as arguments to methods, while the related concept of method argument injection is not widespread and mostly used in the context of automated testing only.
Strategy focuses on intent and encourages you to create an interface with different implementations that obey the same behavioral contract. DI is more about just having an implementation of some behavior and providing it.
With DI you can decompose your program for other reasons than just to be able to swap parts of the implementation. An interface used in DI with only one implementation is very common. A "Strategy" with only one concrete implementation (ever) is not a real problem but is probably closer to DI.
Strategy Pattern in real life
The easiest way to do a bad job with design patterns is to learn a pattern, and then go trying to find places to put it. I did this a few times when I was first learning, and the results were quite frustrating.
The strategy pattern is a solution for a specific shape of problem. That problem is " I need to do things that are basically the same, but there are some variations in the middle". So for now, remember how to do a strategy pattern, and when you see a problem that suggests it, use it.
Related Topics
Python Read File as Stream from Hdfs
How to Install the Yaml Package for Python
Appending List But Error 'Nonetype' Object Has No Attribute 'Append'
Putting a 'Cookie' in a 'Cookiejar'
Writing to a File in a for Loop Only Writes the Last Value
Python Multiple Inheritance Passing Arguments to Constructors Using Super
Unicodeencodeerror: 'Ascii' Codec Can't Encode Character at Special Name
How to Feed Time-Series Data to Stateful Lstm
String Similarity Metrics in Python
How to Set Layer-Wise Learning Rate in Tensorflow
Reading the Target of a .Lnk File in Python
How to Handle an Asymptote/Discontinuity with Matplotlib