You want to implement stateful objects, which have a different set of behaviours according to what state they are in.
This requirement can be achieved with the use of mix-ins. A mix-in is a class which is dynamically inherited by an object. The methods of the mix-in class are thus accessible through the object. This is a clean way of providing objects with standard interfaces.
Chuck Esterbrook has written a discussion of mix-ins, and their implementation in Python, which can be found here: http://www.linuxjournal.com/article.php?sid=4540
The recipe draws from the famous "Lumberjack Song". If you're not familiar with it, all you need to know is that the passage of time modifies the Lumberjack's behaviour.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | # ! /usr/bin/env python
# Lumberjack.py
# Author: D. Haynes
# 9th July 2002
#
# For more details on mix-ins, see
# http://www.linuxjournal.com/article.php?sid=4540
class CambridgeMan:
def roots(self):
return "But I'm forever a Cambridge Man"
class LumberjackInterface:
def __init__(self, methods):
self.methods = list(methods)
def __iter__(self):
return self
def next(self):
try:
return self.methods.pop(0)
except IndexError:
raise StopIteration
class LumberjackBase(CambridgeMan):
def __init__(self, name):
self.bases = self.__class__.__bases__
self.name = name
self.title = "Young %s" % self.name
def _mixIn(self, mixInClass):
self.__class__.__bases__ = (mixInClass,) + self.bases
mixInClass.__init__(self, self.name)
def Interface(self):
"""
Return a list of the names of the methods to be used by
the client.
"""
unpublishedMethods = ("roots", "Interface",
"WishIdBeenAGirlie")
methodType = type(self.Interface)
ifList = []
for i in dir(self):
if (type(getattr(self, i)) == methodType
and not i.startswith('_')):
ifList.append(i)
for i in unpublishedMethods:
ifList.remove(i)
return LumberjackInterface(ifList)
class RookieLumberjackMixIn:
"""
I cut down trees, I eat my lunch, I go to the lavatory.
On Wednesdays I go shopping, and have buttered scones for
tea...
"""
def __init__(self, name):
self.name = name
self.title = "Young %s" % self.name
def CutDownTrees(self):
return "%s cut down a tree." % self.name
def EatMyLunch(self):
return "%s eats his lunch." % self.name
def GoShopping(self):
return "%s has gone shopping." % self.name
def WishIdBeenAGirlie(self):
return 0
class SeasonedLumberjackMixIn:
"""
I cut down trees, I skip and jump, I like to press wild
flowers. I put on womens' clothing, and hang around in bars...
"""
def __init__(self, name):
self.name = name
self.title = "Head Lumberjack %s" % name
def CutDownTrees(self):
return "%s cut down a tree." % self.name
def SkipAndJump(self):
return "%s skips and jumps." % self.name
def HangAroundInBars(self):
return "%s is in the bar." % self.name
def WishIdBeenAGirlie(self):
return 0
class VeteranLumberjackMixIn:
"""
I cut down trees, I wear high heels, suspenders and a bra.
I wish I'd been a girlie, just like my dear Papa...
"""
def __init__(self, name):
self.name = name
self.title = "Old Man %s" % name
def CutDownTrees(self):
return "%s cut down a tree." % self.name
def WearHighHeels(self):
return "%s has stilletos on." % self.name
def WishIdBeenAGirlie(self):
return 1
class LumberjackFactory:
def __init__(self):
pass
def Lumberjack(self, name):
jack = LumberjackBase(name)
jack._mixIn(RookieLumberjackMixIn)
return jack
def display(jack):
print "I'm %s and I can..." % jack.title
for i in jack.Interface():
print i
print "And I do%s wish I was a girlie!" % ("n't" * (1 - jack.
WishIdBeenAGirlie()))
print
def do(jack):
for i in jack.Interface():
print apply(getattr(jack, i))
print
if __name__ == "__main__":
factory = LumberjackFactory()
jack = factory.Lumberjack("Jack")
display(jack)
do(jack)
jack._mixIn(SeasonedLumberjackMixIn)
display(jack)
do(jack)
jack._mixIn(VeteranLumberjackMixIn)
display(jack)
do(jack)
print jack.roots()
|
Core object functionality is provided in the LumberjackBase class. This class may inherit from others in the usual way. However, since we will be manipulating the inheritance of this class, one of the first things a LumberjackBase object does is to store a record of its base classes. The class contains one compulsory method, _mixIn, of which more later. There is also an Interface method in this example, which serves to publish the methods currently available via a simple object which could be pickled and passed via a Queue to other threads or processes.
A factory object is used to assemble a new Lumberjack object from the LumberjackBase class. State is given to this object by mixing in a class which provides its initial functional interface. The mix-in mechanism is achieved using the _mixin method of the LumberjackBase object.
The _mixin method swaps the current mixed-in class for a new one, retaining the core inherited classes. Finally, the __init__ method of the new class is called to perform any mix-in specific initialisation.
The example shows how versatile this approach can be; it allows the stateful objects to inherit from other classes, to overlap state functionality, or to hide methods from the client. One application of this might be in implementing the "Domain Model" pattern to inform a GUI what operations are possible on a selected item.
Engineers don't know their footwear. Of course, that should be 'stiletto'. But you get the point :-D