如何用不同于维基百科示例的方式在Python中编写策略模式?
在2009年的维基百科关于策略模式的条目中,有一个用PHP写的例子。
其他大多数代码示例都是这样做的:
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
在Python的代码中,使用了一种不同的技术,涉及到一个提交按钮。我在想,如果Python的代码也像其他代码示例那样写,会是什么样子。
更新:用Python的第一类函数,能不能写得更简短一些呢?
5 个回答
你说得对,维基百科的例子并没有帮助。它把两个概念搞混了。
策略。
Python的一些特性可以让实现策略变得简单。说“没有必要明确实现这个模式”是不对的。你通常需要实现策略,但Python通过允许你使用函数而不需要在函数外包裹一个类来简化这个过程。
首先,策略。
class AUsefulThing( object ):
def __init__( self, aStrategicAlternative ):
self.howToDoX = aStrategicAlternative
def doX( self, someArg ):
self. howToDoX.theAPImethod( someArg, self )
class StrategicAlternative( object ):
pass
class AlternativeOne( StrategicAlternative ):
def theAPIMethod( self, someArg, theUsefulThing ):
pass # an implementation
class AlternativeTwo( StrategicAlternative ):
def theAPImethod( self, someArg, theUsefulThing ):
pass # another implementation
现在你可以像这样做。
t = AUsefulThing( AlternativeOne() )
t.doX( arg )
它会使用我们创建的策略对象。
其次,Python的替代方案。
class AUsefulThing( object ):
def __init__( self, aStrategyFunction ):
self.howToDoX = aStrategyFunction
def doX( self, someArg ):
self.howToDoX( someArg, self )
def strategyFunctionOne( someArg, theUsefulThing ):
pass # an implementation
def strategyFunctionTwo( someArg, theUsefulThing ):
pass # another implementation
我们可以这样做。
t= AUsefulThing( strategyFunctionOne )
t.doX( anArg )
这也会使用我们提供的策略函数。
这是在回答一个老问题,专门给那些搜索“python 策略模式”的人看的...
在支持一等函数的编程语言中,这种模式几乎不存在。你可能想要利用Python中的这个特性:
def strategy_add(a, b):
return a + b
def strategy_minus(a, b):
return a - b
solver = strategy_add
print solver(1, 2)
solver = strategy_minus
print solver(2, 1)
这种方法非常简洁明了。
另外,别忘了去看看Joe Gregorio在2009年PyCon大会上的演讲,内容是关于Python和设计模式(或者说缺乏设计模式): http://pyvideo.org/video/146/pycon-2009--the--lack-of--design-patterns-in-pyth
这个Python的例子和其他语言的例子差不多。要模拟PHP脚本,可以这样做:
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()
输出结果:
Original execution
Strategy 1
Strategy 2
主要的不同点有:
- 你不需要写其他的类或者实现任何接口。
- 你可以直接传一个函数引用,这个函数会和你想要的方法绑定在一起。
- 这些函数仍然可以单独使用,如果你想的话,原始对象可以有默认的行为(可以用
if func == None
这种方式来实现)。 - 确实,Python的写法简洁优雅。但这样你会失去一些信息;因为没有明确的接口,程序员需要被认为是成年人,知道自己在做什么。
需要注意的是,在Python中有三种动态添加方法的方式:
我刚才给你展示的方式。但这个方法是静态的,不会传递“self”参数。
使用类名:
StrategyExample.execute = func
在这里,所有的实例都会把func
作为execute
方法,并且会把self
作为参数传递。
只绑定到一个实例(使用
types
模块):strat0.execute = types.MethodType(executeReplacement1, strat0)
或者在Python 2中,还需要指定实例的类:
strat0.execute = types.MethodType(executeReplacement1, strat0, StrategyExample)
这样会把新方法绑定到strat0
,而且只有strat0
会这样。像第一个例子一样,但start0.execute()
会把self
作为参数传递。
如果你需要在函数中使用当前实例的引用,那么你可以把第一种和最后一种方法结合起来。如果不需要:
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()
你会得到:
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
所以正确的代码应该是:
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()
这将输出预期的结果:
Strategy Example 0
Strategy Example 1 from execute 1
Strategy Example 2 from execute 2
当然,在这种情况下,这些函数就不能再单独使用了,但仍然可以绑定到任何其他对象的实例上,没有任何接口限制。