principlepythonMajor
Strategy design pattern with various duck type classes
Viewed 0 times
duckwithdesigntypeclassesstrategyvariouspattern
Problem
I've recently picked up the Head First Design Patterns book in an effort to become a more efficient and better Python programmer. Unfortunately, the code examples in this book are in Java.
I'm not the first one that wishes there was a Python version of this book, but I thought that writing the Python code would be a great exercise.
I've tried to implement the Strategy design pattern from Chapter 1 of Head First Design Patterns below in Python. Now, I know that Python is not Java in a general sense, but not in the nitty-gritty sense. So, I'm sure there are more Pythonic things that I can do to this code.
Off the top of my head we could probably implement the
Any other suggestions?
```
#!/usr/bin/env python
################################################################################
# Abstract Duck class and concrete Duck type classes.
################################################################################
class Duck(object):
def __init__(self):
pass
def set_fly_behavior(self, fb):
self.fly_behavior = fb
def set_quack_behavior(self, qb):
self.quack_behavior = qb
def perform_fly(self):
self.fly_behavior.fly()
def perform_quack(self):
self.quack_behavior.quack()
def swim(self):
print "All ducks float, even decoys!"
class MallardDuck(Duck):
def __init__(self):
Duck.__init__(self)
fly_instance = Fly()
self.set_fly_behavior(fly_instance)
quack_instance = Quack()
self.set_quack_behavior(quack_instance)
print "I'm a real Mallard Duck"
class ModelDuck(Duck):
def __init__(self):
Duck.__init__(self)
fly_no_way_instance = FlyNoWay()
self.set_fly_behavior(fly_no_way_instance)
squeak_instance = SqueakQuack()
self.set_quack_behavior(squeak_instance)
print "I'm a Model Duck"
class De
I'm not the first one that wishes there was a Python version of this book, but I thought that writing the Python code would be a great exercise.
I've tried to implement the Strategy design pattern from Chapter 1 of Head First Design Patterns below in Python. Now, I know that Python is not Java in a general sense, but not in the nitty-gritty sense. So, I'm sure there are more Pythonic things that I can do to this code.
Off the top of my head we could probably implement the
Duck class set_fly_behavior() and set_quack_behavior() using the Python property built-in. Any other suggestions?
```
#!/usr/bin/env python
################################################################################
# Abstract Duck class and concrete Duck type classes.
################################################################################
class Duck(object):
def __init__(self):
pass
def set_fly_behavior(self, fb):
self.fly_behavior = fb
def set_quack_behavior(self, qb):
self.quack_behavior = qb
def perform_fly(self):
self.fly_behavior.fly()
def perform_quack(self):
self.quack_behavior.quack()
def swim(self):
print "All ducks float, even decoys!"
class MallardDuck(Duck):
def __init__(self):
Duck.__init__(self)
fly_instance = Fly()
self.set_fly_behavior(fly_instance)
quack_instance = Quack()
self.set_quack_behavior(quack_instance)
print "I'm a real Mallard Duck"
class ModelDuck(Duck):
def __init__(self):
Duck.__init__(self)
fly_no_way_instance = FlyNoWay()
self.set_fly_behavior(fly_no_way_instance)
squeak_instance = SqueakQuack()
self.set_quack_behavior(squeak_instance)
print "I'm a Model Duck"
class De
Solution
The devil’s in the
Your suggestion is good, but you don’t need a property; you can just use a normal attribute. All your setter does is set the variable, so just do that instead. So, this:
Can become this:
Note my use of
Aside: naming behaviours
The names
ABCs
Other than that, you could make your base classes Abstract Base Classes:
This allows you to have a more Pythonic interface for your class.
(Also, note the removal of the empty constructor. Python provides an empty constructor anyway, so don’t bother defining one if you do nothing with it. It’s just useless extra code.)
A functional approach
This is a pretty heavyweight solution for Python. If you know that these behaviours are always just one function, I would argue classes aren’t needed.
Instead, recall that Python functions are first-class objects:
A hybrid approach
We can make this work with one small modification: by using a property to convert functions into methods, so that they can be called normally.
(I’m assuming you need access to
One step further...
Hmm...we’re doing the same thing for both properties. If you want, we can take it a step further.
Let’s generalise it instead:
Just assign the function as needed—perhaps in the constructor of subclasses. Obviously, if you need more complicated behaviours, this might not do. But for simple things, I’d say it’s the better solution.
Constructors for subclassing
Since a duck is useless without a quack and fly behaviour, and all subclasses assign them, it makes sense for the constructor to deal with this:
Or, if you were using your original class’s implementation:
Conclusion
If we wrap all this together, we end up with two implementations:
These implementations remove repetition, and will make this sort of thing much easier to work with in Python overall.
_detailsYour suggestion is good, but you don’t need a property; you can just use a normal attribute. All your setter does is set the variable, so just do that instead. So, this:
self.set_fly_behavior(fly_instance)Can become this:
self._fly_behaviour = fly_instanceNote my use of
self._fly_behaviour. Since it’s is an implementation detail of the class, the best practice is to prefix the variable name with an underscore. This tells programmers that it’s not designed to be used externally.Aside: naming behaviours
The names
perform_quack() and perform_fly() are very un-Pythonic. Why not just quack() and fly()? The perform tells us nothing—except, maybe leaking an implementation detail to the outside (that these delegated). This isn't something a caller needs to care about.ABCs
Other than that, you could make your base classes Abstract Base Classes:
from abc import ABCMeta, abstractmethod
...
class QuackBehavior(object):
__metaclass__ = ABCMeta
@abstractmethod
def quack(self):
pass
@classmethod
def __subclasshook__(cls, Check):
required = ["quack"]
rtn = True
for r in required:
if not any(r in vars(BaseClass) for BaseClass in Check.__mro__):
rtn = NotImplemented
return rtnThis allows you to have a more Pythonic interface for your class.
(Also, note the removal of the empty constructor. Python provides an empty constructor anyway, so don’t bother defining one if you do nothing with it. It’s just useless extra code.)
A functional approach
This is a pretty heavyweight solution for Python. If you know that these behaviours are always just one function, I would argue classes aren’t needed.
Instead, recall that Python functions are first-class objects:
def quack(self):
print("Quack")
def mute_quack(self):
print ">"
some_duck = Duck()
some_duck.quack = mute_quackA hybrid approach
We can make this work with one small modification: by using a property to convert functions into methods, so that they can be called normally.
(I’m assuming you need access to
self in the method; if not, leave out that argument, and it’ll work as-is, like a static method.)import types
class Duck(object):
...
@property
def fly(self):
return self._fly
@fly.setter
def fly(self, value):
self._fly = types.MethodType(value, self)
@property
def quack(self):
return self._quack
@quack.setter
def quack(self, value):
self._quack = types.MethodType(value, self)One step further...
Hmm...we’re doing the same thing for both properties. If you want, we can take it a step further.
Let’s generalise it instead:
import types
def method_property(name):
def getter(self):
return getattr(self, name)
def setter(self, value):
setattr(self, name, types.MethodType(value, self))
return getter, setter
class Duck(object):
...
quack = property(*method_property("_quack"))
fly = property(*method_property("_fly"))Just assign the function as needed—perhaps in the constructor of subclasses. Obviously, if you need more complicated behaviours, this might not do. But for simple things, I’d say it’s the better solution.
Constructors for subclassing
Since a duck is useless without a quack and fly behaviour, and all subclasses assign them, it makes sense for the constructor to deal with this:
class Duck(object):
def __init__(self, fly, quack):
self.fly = fly
self.quack = quack
...
class ModelDuck(Duck):
def __init__(self):
Duck.__init__(self, fly_no_way, squeak_quack)
print "I'm a Model Duck"Or, if you were using your original class’s implementation:
class Duck(object):
def __init__(self, fly, quack):
self._fly_behaviour = fly
self._quack_behaviour = quack
...
class ModelDuck(Duck):
def __init__(self):
Duck.__init__(self, FlyNoWay(), SqueakQuack())
print "I'm a Model Duck"Conclusion
If we wrap all this together, we end up with two implementations:
- function based, and
- class based.
These implementations remove repetition, and will make this sort of thing much easier to work with in Python overall.
Code Snippets
self.set_fly_behavior(fly_instance)self._fly_behaviour = fly_instancefrom abc import ABCMeta, abstractmethod
...
class QuackBehavior(object):
__metaclass__ = ABCMeta
@abstractmethod
def quack(self):
pass
@classmethod
def __subclasshook__(cls, Check):
required = ["quack"]
rtn = True
for r in required:
if not any(r in vars(BaseClass) for BaseClass in Check.__mro__):
rtn = NotImplemented
return rtndef quack(self):
print("Quack")
def mute_quack(self):
print "<< Silence >>"
some_duck = Duck()
some_duck.quack = mute_quackimport types
class Duck(object):
...
@property
def fly(self):
return self._fly
@fly.setter
def fly(self, value):
self._fly = types.MethodType(value, self)
@property
def quack(self):
return self._quack
@quack.setter
def quack(self, value):
self._quack = types.MethodType(value, self)Context
StackExchange Code Review Q#20718, answer score: 32
Revisions (0)
No revisions yet.