Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write Strategy Pattern in Python differently than example in Wikipedia?

In the 2009 Wikipedia entry for the Strategy Pattern, there's a example written in PHP.

Most other code samples do something like:

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 

In the Python code a different technique is used with a Submit button. I wonder what the Python code will look like if it also did it the way the other code samples do.

Update: Can it be shorter using first-class functions in Python?

like image 973
nonopolarity Avatar asked Jun 08 '09 08:06

nonopolarity


People also ask

What is strategy pattern in Python?

Strategy is a behavioral design pattern that turns a set of behaviors into objects and makes them interchangeable inside original context object. The original object, called context, holds a reference to a strategy object and delegates it executing the behavior.

Which pattern allows different formatting of an algorithms?

In computer programming, the strategy pattern (also known as the policy pattern) is a behavioral software design pattern that enables selecting an algorithm at runtime.

What type of pattern is strategy?

Strategy is a behavioral design pattern that lets you define a family of algorithms, put each of them into a separate class, and make their objects interchangeable.


1 Answers

The example in Python is not so different of the others. To mock the PHP script:

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() 

Output:

Original execution Strategy 1 Strategy 2 

The main differences are:

  • You don't need to write any other class or implement any interface.
  • Instead you can pass a function reference that will be bound to the method you want.
  • The functions can still be used separately, and the original object can have a default behavior if you want to (the if func == None pattern can be used for that).
  • Indeed, it's clean short and elegant as usual with Python. But you lose information; with no explicit interface, the programmer is assumed as an adult to know what they are doing.

Note that there are 3 ways to dynamically add a method in Python:

  • The way I've shown you. But the method will be static, it won't get the "self" argument passed.

  • Using the class name:

    StrategyExample.execute = func

Here, all the instance will get func as the execute method, and will get self passed as an argument.

  • Binding to an instance only (using the types module):

    strat0.execute = types.MethodType(executeReplacement1, strat0)

    or with Python 2, the class of the instance being changed is also required:

    strat0.execute = types.MethodType(executeReplacement1, strat0, StrategyExample)

This will bind the new method to strat0, and only strat0, like with the first example. But start0.execute() will get self passed as an argument.

If you need to use a reference to the current instance in the function, then you would combine the first and the last method. If you do not:

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() 

You will get:

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 

So the proper code would be:

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() 

This will output the expected result:

Strategy Example 0 Strategy Example 1 from execute 1 Strategy Example 2 from execute 2 

Of course, in the case the functions cannot be used stand alone anymore, but can still be bound to any other instance of any object, without any interface limitation.

like image 88
e-satis Avatar answered Oct 17 '22 18:10

e-satis