Strategy Design Pattern

D

Daniel Santa Cruz

Hello all,

I've been trying to go over my OO Patterns book, and I decided to try
to implement them in Python this time around. I figured this would
help me learn the language better.

Well, I've gotten stuck with my first go at OO patterns with Python. I
guess it goes without say that some of the stuff that are taken for
granted in most of the books (ie. Interfaces, Abstract classes) don't
really apply to Python per say, but the idea behind the patterns can be
extracted out still. In the specific case of the Strategy pattern, I
think the goal is to abstract out of the class an algorithm that can
then be reused by some of the inherited classes. This way we don't
repeat ourselves. Maybe I got this all wrong...

I'm at a loss at how I can do this with Python, any pointers would be
more than welcomed!

To aid commenters... we can use the example used in "Head First Design
Patterns". I've implemented this simple patter in Java and .NET... now
in python. I can't draw UML here, so I'll try to pseudo talk what the
example has:

Abstract Base Class: Duck
+ FlyBehavior _fly
+ swim()
+ fly() -> calls _fly.fly()

Interface: FlyBehavior
+ fly()

Concrete Interface FlyHigh (implements FlyBehavior):
+ fly()

Concrete Class Duck1 (Inherits Duck):
+ Constructor: _fly = new FlyHigh()

Daniel.
 
A

Alex Martelli

Daniel Santa Cruz said:
I'm at a loss at how I can do this with Python, any pointers would be
more than welcomed!

Check out my homepage, www.aleax.it, you'll find several PDFs for my
various presentations over the years: many have to do with DPs and
Python, and I specifically addressed Strategy in a couple of them.


Alex
 
K

Kay Schluehr

Daniel said:
Hello all,

I've been trying to go over my OO Patterns book, and I decided to try
to implement them in Python this time around. I figured this would
help me learn the language better.

I guess it might help clarifying what OO is about since you see what
happens when you try to transform solutions across languages with very
different type systems.
Well, I've gotten stuck with my first go at OO patterns with Python. I
guess it goes without say that some of the stuff that are taken for
granted in most of the books (ie. Interfaces, Abstract classes) don't
really apply to Python per say, but the idea behind the patterns can be
extracted out still. In the specific case of the Strategy pattern, I
think the goal is to abstract out of the class an algorithm that can
then be reused by some of the inherited classes. This way we don't
repeat ourselves. Maybe I got this all wrong...

Well, yes, I disagree ;) From my point of view the StrategyPattern is
used to decouple the consumer/client/user of an algorithm from it's
implementation. If you need variations of the implementation of your
algo you don't want variations in the user. It's not about a tower of
abstractions in the Strategy itself. Those abstractions are there for
the purpose of enabling polymorphism.

If you want a straightforward translation you start with a Strategy
class and derive strategies from it:

class Strategy(object):
def Evaluate(self):
raise NotImplementedError # making it abstract

class StrategyA(Strategy):
def Evaluate(self):
print "Evaluate StrategyA"

class StrategyB(Strategy):
def Evaluate(self):
print "Evaluate StrategyB"

This is very clean OO but you don't actually need the Strategy base
class because Pythons type system does not enforce a well known type at
compile time. In some sense also the StrategyX classes are overhead
because you can pass the Evaluate methods directly to the consumer:
functions are always first class objects. So the Strategy might be
reduced to:

def EvaluateA(self):
print "Evaluate StrategyA"

def EvaluateB(self):
print "Evaluate StrategyB"

def consumer(evaluate):
evaluate()
Evaluate StrategyB
 
B

Ben Sizer

Daniel said:
Well, I've gotten stuck with my first go at OO patterns with Python. I
guess it goes without say that some of the stuff that are taken for
granted in most of the books (ie. Interfaces, Abstract classes) don't
really apply to Python per say, but the idea behind the patterns can be
extracted out still.

In the original "Design Patterns" book, the authors pointed out that
although patterns are generally language-agnostic, the need for
formalised patterns to achieve a given goal is not. One example is
inheritance - if you're programming in C then you'd need a formal
pattern to model inheritance effectively, but in many other languages
it's built in. It may be the case that Python gives you the tools to
make some other patterns effectively redundant.
In the specific case of the Strategy pattern, I
think the goal is to abstract out of the class an algorithm that can
then be reused by some of the inherited classes. This way we don't
repeat ourselves. Maybe I got this all wrong...

The goal is to allow an object to have a certain behaviour which uses
an algorithm you can change by changing the object that performs that
algorithm. It essentially means you can change the algorithm later
without changing the object.

In Python, this is quite simple - decide upon an interface that the
algorithm should provide, and then assign an object that implements
that interface to your object. The object will then pass its data to
the interface methods on whichever algorithm object it's been given.
To aid commenters... we can use the example used in "Head First Design
Patterns". I've implemented this simple patter in Java and .NET... now
in python. I can't draw UML here, so I'll try to pseudo talk what the
example has:

Abstract Base Class: Duck
+ FlyBehavior _fly
+ swim()
+ fly() -> calls _fly.fly()

Interface: FlyBehavior
+ fly()

Concrete Interface FlyHigh (implements FlyBehavior):
+ fly()

Concrete Class Duck1 (Inherits Duck):
+ Constructor: _fly = new FlyHigh()

In this example, you can just assign a FlyHigh function to your Duck1 -
no Duck or FlyBehavior classes needed. The pattern is almost pointless
in situations like this when your language allows you to quickly and
easily assign functions between objects. However if, as is common, you
need more than 1 function in the interface, you can put them in an
object and assign that.

class FlyHigh(object):
def TakeOff(self, bird):
print "take off to fly high"
def Land(self, bird):
print "land from flying high"

class FlyLow(object):
def TakeOff(self, bird):
print "take off to fly low"
def Land(self, bird):
print "land from flying low"

class Duck(object):
def __init__(self, flyingBehavior):
self.flyBehavior = flyingBehavior
def TakeOff(self):
self.flyBehavior.TakeOff(self)
def Land(self):
self.flyBehavior.Land(self)

lowFlyingDuck = Duck(FlyLow())
highFlyingDuck = Duck(FlyHigh())


Note that I don't claim the above code is necessarily optimal (or even
correct!) but it should show that half of the Strategy pattern
boilerplate is unnecessary in Python. You can even use certain Python
tricks to automatically delegate calls on Duck to the behavior classes
without typing those out individually, for example.
 
G

Gerard Flanagan

Daniel said:
Hello all,

I've been trying to go over my OO Patterns book, and I decided to try
to implement them in Python this time around. I figured this would
help me learn the language better.

Well, I've gotten stuck with my first go at OO patterns with Python. I
guess it goes without say that some of the stuff that are taken for
granted in most of the books (ie. Interfaces, Abstract classes) don't
really apply to Python per say, but the idea behind the patterns can be
extracted out still. In the specific case of the Strategy pattern, I
think the goal is to abstract out of the class an algorithm that can
then be reused by some of the inherited classes. This way we don't
repeat ourselves. Maybe I got this all wrong...

I'm at a loss at how I can do this with Python, any pointers would be
more than welcomed!


This is my understanding of the Strategy Pattern:

(Perhaps I have a SudokuSolver class that will solve *any* Sudoku
puzzle, but it's very slow for simple puzzles because of some
unavoidable overhead related only to the more difficult puzzles - so I
want to try a simpler but faster method first, if it doesn't succeed
then use the more complex method.)

class SudokuGrid(object):
def __init__(self, solver=None):
self.grid = [[-1]*9]*9
if solver is not None:
self.solver = solver
else:
self.solver = SimpleSudokuSolver()

def solve(self):
self.solver.sudoku = self
self.solver.solve()

class SimpleSudokuSolver(object):
sudoku = None
def solve(self):
print 'solving %s ...simple algorithm...' % repr(self.sudoku)

class DifficultSudokuSolver(object):
sudoku = None
def solve(self):
print 'solving %s ...difficult algorithm...' %
repr(self.sudoku)

s = SudokuGrid()

s.solve()

s.solver = DifficultSudokuSolver()

s.solve()

solving <__main__.SudokuGrid object at 0x0117D390> ...simple
algorithm...
solving <__main__.SudokuGrid object at 0x0117D390> ...difficult
algorithm...

HTH

Gerard
 
B

bruno at modulix

Daniel said:
Hello all,

I've been trying to go over my OO Patterns book, and I decided to try
to implement them in Python this time around. I figured this would
help me learn the language better.

Well, I've gotten stuck with my first go at OO patterns with Python. I
guess it goes without say that some of the stuff that are taken for
granted in most of the books (ie. Interfaces, Abstract classes) don't
really apply to Python per say, but the idea behind the patterns can be
extracted out still.

A good part of the GoF patterns are meant to add flexibility to static
languages - translating them directly in a dynamic language may not be
such a good idea. Regarding abstract base classes (Java's interfaces
being a special case of abc), they are mostly used in static languages
to provide support for polymorphic dispatch. This is not needed in
Python, where polymorphic dispatch is not tied to inheritance.

FWIW, it's still possible to have abc in Python:

class Abc(object):
def abstractMethod(self, args):
raise NotImplementedError("Abc is an abstract class")
In the specific case of the Strategy pattern, I
think the goal is to abstract out of the class an algorithm that can
then be reused by some of the inherited classes. This way we don't
repeat ourselves. Maybe I got this all wrong...

The first part (abstract an algorithm out of the class) is ok. The real
goal is to allow to dynamically select the algorithm to use based on
runtime conditions (ie: user prefs, platform-specific stuff, size of a
file, phase of the moon, whatnot...).
I'm at a loss at how I can do this with Python, any pointers would be
more than welcomed!

Hint 1: Python's functions are objects
Hint 2: Dynamically attaching a method to an object (ie : not to the
whole class) is easy as pie (search this group...).
Hint 3: Any object having a __call__() method is callable.
 
M

Marc 'BlackJack' Rintsch

Daniel Santa said:
In the specific case of the Strategy pattern, I think the goal is to
abstract out of the class an algorithm that can then be reused by some
of the inherited classes. This way we don't repeat ourselves. Maybe I
got this all wrong...

IMHO yes. The goal isn't to reuse the algorithm elsewhere but to plug in
different algorithms into the "main class", which is called `context` in
the GoF book.
Abstract Base Class: Duck
+ FlyBehavior _fly
+ swim()
+ fly() -> calls _fly.fly()

Interface: FlyBehavior
+ fly()

Concrete Interface FlyHigh (implements FlyBehavior): + fly()

Concrete Class Duck1 (Inherits Duck): + Constructor: _fly = new
FlyHigh()

No need to make a subclass of `Duck` and the base class should not be
abstract but take a strategy in the constructor instead.

Silly, contrived example with ducks (the `context`):

class Duck(object):
def __init__(self, fly_strategy=lambda duck: None):
self._fly = fly_strategy
self.battery = 100.0 # Percent.

def fly(self):
self._fly(self)

A dumb strategy:

def flap_as_fast_as_you_can(duck):
duck.battery -= 5

And a more sophisticated one:

def ExploitThermodynamics(object):
def __init__(self):
# Initialize data structures to keep track of wind, target etc.

def __call__(self, duck):
# Check wind, update data and change the heading of the duck
# accordingly.
duck.battery -= used_power

Now some ducks:

no_fly_duck = Duck()
fast_but_soon_tired_duck = Duck(flap_as_fast_as_you_can)
gliding_duck = Duck(ExploitThermodynamics())

If the `strategy` is a callable one can decide if a simple function is
sufficient or if the `strategy` needs some state that must be preserved
between calls to the strategy object.

Ciao,
Marc 'BlackJack' Rintsch
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top