How to Write Strategy Pattern in Python Differently Than Example in Wikipedia

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



Leave a reply



Submit