This is a Python implementation of the observer pattern described by Gamma et. al. It defines a one-to many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
The example should output: Setting Data 1 = 10 DecimalViewer: Subject Data 1 has data 10 HexViewer: Subject Data 1 has data 0xa Setting Data 2 = 15 HexViewer: Subject Data 2 has data 0xf DecimalViewer: Subject Data 2 has data 15 Setting Data 1 = 3 DecimalViewer: Subject Data 1 has data 3 HexViewer: Subject Data 1 has data 0x3 Setting Data 2 = 5 HexViewer: Subject Data 2 has data 0x5 DecimalViewer: Subject Data 2 has data 5 Detach HexViewer from data1 and data2. Setting Data 1 = 10 DecimalViewer: Subject Data 1 has data 10 Setting Data 2 = 15 DecimalViewer: Subject Data 2 has data 15
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 | class Subject:
def __init__(self):
self._observers = []
def attach(self, observer):
if not observer in self._observers:
self._observers.append(observer)
def detach(self, observer):
try:
self._observers.remove(observer)
except ValueError:
pass
def notify(self, modifier=None):
for observer in self._observers:
if modifier != observer:
observer.update(self)
# Example usage
class Data(Subject):
def __init__(self, name=''):
Subject.__init__(self)
self.name = name
self.data = 0
def setData(self, data):
self.data = data
self.notify()
def getData(self):
return self.data
class HexViewer:
def update(self, subject):
print 'HexViewer: Subject %s has data 0x%x' % (subject.name, subject.getData())
class DecimalViewer:
def update(self, subject):
print 'DecimalViewer: Subject %s has data %d' % (subject.name, subject.getData())
# Example usage...
def main():
data1 = Data('Data 1')
data2 = Data('Data 2')
view1 = DecimalViewer()
view2 = HexViewer()
data1.attach(view1)
data1.attach(view2)
data2.attach(view2)
data2.attach(view1)
print "Setting Data 1 = 10"
data1.setData(10)
print "Setting Data 2 = 15"
data2.setData(15)
print "Setting Data 1 = 3"
data1.setData(3)
print "Setting Data 2 = 5"
data2.setData(5)
print "Detach HexViewer from data1 and data2."
data1.detach(view2)
data2.detach(view2)
print "Setting Data 1 = 10"
data1.setData(10)
print "Setting Data 2 = 15"
data2.setData(15)
if __name__ == '__main__':
main()
|
The classes shown are meant for subclassing. The subject class is a bit modified in relation to the pattern desribed by Gamma et. al. When the notify method is called it also takes a modifier argument. This is convenient if you don't want an observer which has modified the subject to be updated again.
Tightening up... I would humbly suggest removing the Observer class, as it adds no value to the program. Also modifying the attach command to help avoid double additions would be a good thing:
Removed observer class. Based on the comment, I removed the Observer Class. Additionally I changed the try-except clause, to except ValueError instead of all errors.
I think it was wrong to remove the Observer class. Observer was a kind of interface that documented the behaviour of an observer. Of course, in practice it isn't needed _because this is python and it is a dynamic language_, but it makes it clearer to the reader/maintainer.
subclassing necessary? I disagree, forcing observers to be subclassed from an IObserver class is unecessary. As long as your Subject class is well documented, there shouldn't be any confusion. If you really can't trust the observers, why not add extra checks to the attach method to enforce the contract.
Those checks don't cover every bad observer, but i'm sure it's possible with some extra work.
Here's an update, 9 years later, to take advantage of some newer language features added to python since you wrote your post.