In the 2009 Wikipedia entry for the Strategy Pattern, there's a example written in PHP.
Most other code samples do something like:
a = Context.new(StrategyA.new) a.execute #=> Doing the task the normal way b = Context.new(StrategyB.new) b.execute #=> Doing the task alternatively c = Context.new(StrategyC.new) c.execute #=> Doing the task even more alternative
In the Python code a different technique is used with a Submit button. I wonder what the Python code will look like if it also did it the way the other code samples do.
Update: Can it be shorter using first-class functions in Python?
Strategy is a behavioral design pattern that turns a set of behaviors into objects and makes them interchangeable inside original context object. The original object, called context, holds a reference to a strategy object and delegates it executing the behavior.
In computer programming, the strategy pattern (also known as the policy pattern) is a behavioral software design pattern that enables selecting an algorithm at runtime.
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.
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:
if func == None
pattern can be used for that).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.
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