web-dev-qa-db-ja.com

Pythonでの戦略パターンの記述方法は、Wikipediaの例とは異なりますか?

戦略パターンの2009 Wikipediaエントリには、例 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でファーストクラスの関数を使用すると、より短くできますか?

39
nonopolarity

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でメソッドを動的に追加するには3つの方法があることに注意してください。

  • 私があなたに見せた方法。しかし、メソッドは静的であり、「自己」引数が渡されません。

  • クラス名を使用する:

    _StrategyExample.execute = func_

ここでは、すべてのインスタンスがfuncexecuteメソッドとして取得し、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
_

もちろん、関数をスタンドアロンで使用できなくなった場合でも、インターフェイスの制限なしに、オブジェクトの他のインスタンスにバインドできます。

70
e-satis

「python戦略パターン」を検索してここに到達した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

44

そうです、ウィキペディアの例は役に立ちません。それは二つのことを膨らませます。

  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

これで、このようなことができるようになります。

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 )

これも、提供した戦略関数を使用します。

33
S.Lott

明確にするために、私はまだ擬似インターフェースを使用します:

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!"
10
Nicolas Dumazet

Pythonで Head First Design Pattern の第1章(戦略パターンをカバー)の「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()
2
Saurabh Verma