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

nop*_*ole 37 python design-patterns

在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
Run Code Online (Sandbox Code Playgroud)

在Python代码中,使用不同的技术和Submit按钮.我想知道如果Python代码也按照其他代码示例的方式执行它会是什么样子.

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

e-s*_*tis 67

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()
Run Code Online (Sandbox Code Playgroud)

输出:

Original execution
Strategy 1
Strategy 2
Run Code Online (Sandbox Code Playgroud)

主要区别是:

  • 您不需要编写任何其他类或实现任何接口.
  • 相反,您可以传递将绑定到所需方法的函数引用.
  • 这些函数仍然可以单独使用,如果您愿意,原始对象可以具有默认行为(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()
Run Code Online (Sandbox Code Playgroud)

你会得到:

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
Run Code Online (Sandbox Code Playgroud)

所以正确的代码是:

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()
Run Code Online (Sandbox Code Playgroud)

这将输出预期结果:

Strategy Example 0
Strategy Example 1 from execute 1
Strategy Example 2 from execute 2
Run Code Online (Sandbox Code Playgroud)

当然,在这种情况下,函数不能再单独使用,但仍然可以绑定到任何对象的任何其他实例,而没有任何接口限制.


Chr*_*eau 40

为搜索"蟒蛇战略模式"的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)
Run Code Online (Sandbox Code Playgroud)

这种方法非常简洁.

此外,请务必查看Joe Gregorio的PyCon 2009关于Python和设计模式(或缺乏)的讨论:http://pyvideo.org/video/146/pycon-2009--thelack-of--design-模式功能于pyth

  • 那为什么我们在 python 中有类呢?您有时设计模式在大型项目中很有帮助,您无法摆脱它:) (2认同)
  • 工作[link](http://pyvideo.org/video/146/pycon-2009--the--lack-of--design-patterns-in-pyth).在其他视频中删除. (2认同)
  • @vivek One 没有专门用于促进设计模式的 Python 或任何其他语言的类。许多事物都可以通过类很好地表示。然而,要求类来表示例如命令或策略模式是一个弱点。任何具有适当闭包的语言都可以不需要。大型项目并不是专门针对课程的要求。许多语言根本没有类,但却用于创建大型项目。当然,类是 Python 中表达数据类型的唯一方法,因此您无法摆脱它们。 (2认同)

S.L*_*ott 32

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

  1. 战略.

  2. Python的特性简化了Strategy的实现."没有必要明确地实现此模式"语句是不正确的.您经常需要实现策略,但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
Run Code Online (Sandbox Code Playgroud)

现在你可以做这样的事情了.

t = AUsefulThing( AlternativeOne() )
t.doX( arg )
Run Code Online (Sandbox Code Playgroud)

它将使用我们创建的策略对象.

第二,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
Run Code Online (Sandbox Code Playgroud)

我们做得到.

t= AUsefulThing( strategyFunctionOne )
t.doX( anArg )
Run Code Online (Sandbox Code Playgroud)

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

  • @Arrieta:使用两者.推荐两者.它们有不同的应用.如果*策略*有多种方法或者(某种程度上)是有状态的,那么你需要第一种方法.在更典型的情况下,第二种工作正常.如果您正在与许多Java或C++程序员合作,那么您必须使用第一个,因为第二个会混淆它们. (3认同)

Nic*_*zet 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!"
Run Code Online (Sandbox Code Playgroud)

  • 这是模板模式,而不是策略 (2认同)

Sau*_*rma 6

我尝试用Python转换Head First设计模式第一章(涵盖策略模式)中的“Duck”示例:

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()
Run Code Online (Sandbox Code Playgroud)