如何在Python中编写策略模式与维基百科中的示例不同?

时间:2009-06-08 08:58:41

标签: python design-patterns

在2009年维基百科的战略模式条目中,有一个例子written in 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代码中,一个不同的技术与Submit按钮一起使用。我想知道如果Python代码也按照其他代码示例的方式执行它会是什么样子。

更新:使用Python中的第一类函数可以缩短它吗?

5 个答案:

答案 0 :(得分:65)

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中动态添加方法:

  • 我向你展示的方式。但该方法将是静态的,它不会通过“自我”参数。

  • 使用班级名称:

    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

当然,如果函数不能再单独使用,但仍然可以绑定到任何对象的任何其他实例,没有任何接口限制。

答案 1 :(得分:39)

为那些搜索过“蟒蛇战略模式”的Google员工回答了一个老问题并落到了这里......

这种模式在支持一流功能的语言中几乎不存在。您可能需要考虑在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的PyCon 2009关于Python和设计模式(或缺乏)的讨论:http://pyvideo.org/video/146/pycon-2009--the--lack-of--design-patterns-in-pyth

答案 2 :(得分:31)

你是对的,维基百科的例子没有帮助。它混淆了两件事。

  1. <强>策略

  2. 简化策略实施的Python功能。 “没有必要明确地实现此模式”语句是不正确的。您经常需要实现策略,但Python通过允许您使用函数来简化这一过程,而不需要在函数周围使用类包装器的开销。

  3. 首先,策略

    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 )
    

    这也将使用我们提供的策略功能。

答案 3 :(得分:9)

为清楚起见,我仍然会使用伪接口:

class CommunicationStrategy(object):
    def execute(self, a, b):
        raise NotImplementedError('execute')

class ConcreteCommunicationStrategyDuck(CommunicationStrategy):
    def execute(self, a, b):
        print "Quack Quack"

class ConcreteCommunicationStrategyCow(CommunicationStrategy):
    def execute(self, a, b):
        print "Mooo"

class ConcreteCommunicationStrategyFrog(CommunicationStrategy):
    def execute(self, a, b):
        print "Ribbit! Ribbit!"

答案 4 :(得分:1)

我试图改变鸭子&#39; Python中Head First Design Pattern的第1章(涵盖战略模式)的例子:

class FlyWithRocket():
    def __init__(self):
        pass
    def fly(self):
        print 'FLying with rocket'

class FlyWithWings():
    def __init__(self):
        pass
    def fly(self):
        print 'FLying with wings'

class CantFly():
    def __init__(self):
        pass
    def fly(self):
        print 'I Cant fly'

class SuperDuck:
    def __init__(self):
        pass
    def setFlyingBehaviour(self, fly_obj):
        self.fly_obj = fly_obj
    def perform_fly(self):
        self.fly_obj.fly()

if __name__ == '__main__':
    duck = SuperDuck()
    fly_behaviour = FlyWithRocket()
    #fly_behaviour = FlyWithWings()
    duck.setFlyingBehaviour(fly_behaviour)
    duck.perform_fly()