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.