Separate pattern implementation from your code so that you can reuse the implementation elsewhere.
The following code is an example that shows a reusable implementation of the Observer pattern of the GoF book.
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 | ##########
# pattern_impl.py
##########
from installmethod import installmethod # the installmethod from recipe: 223613
class ObserverPattern:
"""
A reusable implementation of the Observer pattern.
"""
theSubject = None
observers = {}
class Subject:
def __init__(self):
self.observers = []
def attach(self, observer):
self.observers.append(observer)
def detach(self, observer):
self.observers.remove(observer)
def notify(self):
for observer in self.observers:
observer.update(self)
def decoration(self):
self.decorated_trigger()
self.notify()
class Observer:
def __init__(self, subject):
subject.attach(self)
def update(self, observer):
currentState = observer.get_current_state()
self.react_to_observation(currentState)
def specify_subject(self, subject):
self.theSubject = subject
self.make_generalization(subject, self.Subject)
def add_observer(self, observer):
self.observers[observer.__name__] = observer
self.make_generalization(observer, self.Observer)
def make_generalization(self, childClass, parentClass):
bases = list(childClass.__bases__)
bases.append(parentClass)
childClass.__bases__ = tuple(bases)
def make_observation(self, changeObservation, changeReaction):
func = getattr(self.theSubject, changeObservation)
installmethod(func, self.theSubject, "get_current_state")
for observer in self.observers.keys():
func = getattr(self.observers[observer], changeReaction)
installmethod(func, self.observers[observer], "react_to_observation")
def add_trigger(self, trigger):
func = getattr(self.theSubject, trigger)
installmethod(func, self.theSubject, "decorated_trigger")
func = getattr(self.theSubject, "decoration")
installmethod(func, self.theSubject, trigger)
##########
# example.py
##########
class ClockTimer:
def get_time(self):
# get current state of the subject
return self.currentTime
def tick(self):
# update internal time-keeping state
import time
self.currentTime = time.ctime()
class DigitalClock:
def draw(self, currentTime):
# display currentTime as a digital clock
print "DigitalClock: current time is", currentTime
class AnalogClock:
def draw(self, currentTime):
# display currentTime as an analog clock
print "AnalogClock: current time is", currentTime
if __name__ == '__main__':
from pattern_impl import ObserverPattern
observerPattern = ObserverPattern()
observerPattern.specify_subject(ClockTimer)
observerPattern.add_observer(DigitalClock)
observerPattern.add_observer(AnalogClock)
observerPattern.make_observation("get_time", "draw")
observerPattern.add_trigger("tick")
aTimer = ClockTimer()
dClock = DigitalClock(aTimer)
aClock = AnalogClock(aTimer)
import time
for i in range(10):
print "\nTick!"
aTimer.tick()
time.sleep(1)
|
In the example above, the ObserverPattern class represents the reusable implementation of the Observer pattern. The skeleton of the Observer pattern, which consists of Subject and Observer, is implemented as inner classes of the ObserverPattern class. Besides, the ObserverPattern class provides the functionality that manipulates its target for pattern specialization. The "installmethod" used in the ObserverPattern class is from recipe: 223613 ( http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/223613 ).
Separating pattern implementation from the code may bring some advantages: * The pattern implementation can be reusable. * The use of pattern are documented explicitly in the code. * You can change/modify the pattern implementation without affecting its target.
References: * Implementing Patterns by Jiri Soukup: http://www.codefarms.com/publications/papers/patterns.html * In-Code modelling: http://incode.sourceforge.net/index.html