戦略パターンの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でファーストクラスの関数を使用すると、より短くできますか?
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でメソッドを動的に追加するには3つの方法があることに注意してください。
私があなたに見せた方法。しかし、メソッドは静的であり、「自己」引数が渡されません。
クラス名を使用する:
_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
_
もちろん、関数をスタンドアロンで使用できなくなった場合でも、インターフェイスの制限なしに、オブジェクトの他のインスタンスにバインドできます。
「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
そうです、ウィキペディアの例は役に立ちません。それは二つのことを膨らませます。
戦略。
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 )
これも、提供した戦略関数を使用します。
明確にするために、私はまだ擬似インターフェースを使用します:
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!"
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()