Welcome, guest | Sign In | My Account | Store | Cart

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.

Python, 111 lines
  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