Decoupling code behavior
Observer, and a category of
callbacks called multiple dispatching (not in Design
Patterns) including the Visitor from Design Patterns.
Add Comment
Like the other forms of callback, this
contains a hook point where you can change code. The difference is in the
observers completely dynamic nature. It is often used for the specific
case of changes based on other objects change of state, but is also the
basis of event management. Anytime you want to decouple the source of the call
from the called code in a completely dynamic way.
Add Comment
The
observer pattern solves a fairly
common problem: What if a group of objects needs to update themselves when some
object changes state? This can be seen in the model-view aspect of
Smalltalks MVC (model-view-controller), or the almost-equivalent
Document-View Architecture. Suppose that you have some data (the
document) and more than one view, say a plot and a textual view.
When you change the data, the two views must know to update themselves, and
thats what the observer facilitates. Its a common enough problem
that its solution has been made a part of the standard java.util library.
Add Comment
There are two types of objects used to
implement the observer pattern in Python. The
Observable class keeps track of everybody who wants
to be informed when a change happens, whether the state has
changed or not. When someone says OK, everybody should check and
potentially update themselves, the Observable class performs this
task by calling the notifyObservers( ) method
for each one on the list. The notifyObservers( ) method is part of
the base class Observable.
Add Comment
There are actually two things that
change in the observer pattern: the quantity of observing objects and the
way an update occurs. That is, the observer pattern allows you to modify both of
these without affecting the surrounding code.
Add Comment
-------------
Observer is an
interface class that only has one member function,
update( ). This function is called by the object thats being
observed, when that object decides its time to update all its observers. The
arguments are optional; you could have an update( ) with no
arguments and that would still fit the observer pattern; however this is more
generalit allows the observed object to pass the object that caused the
update (since an Observer may be registered with more than one observed
object) and any extra information if thats helpful, rather than forcing
the Observer object to hunt around to see who is updating and to fetch
any other information it needs.
Add Comment
The observed object that
decides when and how to do the updating will be called the Observable.
Add Comment
Observable has a flag to indicate
whether its been changed. In a simpler design, there would be no flag; if
something happened, everyone would be notified. The flag allows you to wait, and
only notify the Observers when you decide the time is right. Notice,
however, that the control of the flags state is protected, so that
only an inheritor can decide what constitutes a change, and not the end user of
the resulting derived Observer class.
Add Comment
Most of the work is done in
notifyObservers( ). If the changed flag has not been set,
this does nothing. Otherwise, it first clears the changed flag so
repeated calls to notifyObservers( ) wont waste time. This is
done before notifying the observers in case the calls to update( )
do anything that causes a change back to this Observable object. Then it
moves through the set and calls back to the update( ) member
function of each Observer.
Add Comment
At first it may appear that you can use
an ordinary Observable object to manage the updates. But this
doesnt work; to get an effect, you must inherit from
Observable and somewhere in your derived-class code call
setChanged( ). This is the member function
that sets the changed flag, which means that when you call
notifyObservers( ) all of the observers will,
in fact, get notified. Where you call setChanged( ) depends
on the logic of your program.
Add Comment
Since Python doesnt have standard
library components to support the observer pattern (like Java does), we must
first create one. The simplest thing to do is translate the Java standard
library Observer and Observable classes. This also provides easier
translation from Java code that uses these libraries.
Add Comment
In trying to do this, we encounter a
minor snag, which is the fact that Java has a synchronized keyword that
provides built-in support for thread synchronization. We could certainly
accomplish the same thing by hand, using code like this:
Add Comment
import threading class ToSynch: def __init__(self): self.mutex = threading.RLock() self.val = 1 def aSynchronizedMethod(self): self.mutex.acquire() try: self.val += 1 return self.val finally: self.mutex.release()
But this
rapidly becomes tedious to write and to read. Peter Norvig provided me with a
much nicer solution:
Add Comment
#: util:Synchronization.py '''Simple emulation of Java's 'synchronized' keyword, from Peter Norvig.''' import threading def synchronized(method): def f(*args): self = args[0] self.mutex.acquire(); # print method.__name__, 'acquired' try: return apply(method, args) finally: self.mutex.release(); # print method.__name__, 'released' return f def synchronize(klass, names=None): """Synchronize methods in the given class. Only synchronize the methods whose names are given, or all methods if names=None.""" if type(names)==type(''): names = names.split() for (name, val) in klass.__dict__.items(): if callable(val) and name != '__init__' and \ (names == None or name in names): # print "synchronizing", name klass.__dict__[name] = synchronized(val) # You can create your own self.mutex, or inherit # from this class: class Synchronization: def __init__(self): self.mutex = threading.RLock() #:~
The synchronized( )
function takes a method and wraps it in a function that adds the mutex
functionality. The method is called inside this function:
Add Comment
return apply(method, args)
and
as the return statement passes through the finally clause, the
mutex is released.
Add Comment
This is in some ways the Decorator
design pattern, but much simpler to create and use. All you have to say
is:
myMethod = synchronized(myMethod)
To
surround your method with a mutex.
Add Comment
synchronize( ) is a
convenience function that applies synchronized( ) to an entire
class, either all the methods in the class (the default) or selected methods
which are named in a string as the second argument.
Add Comment
Finally, for synchronized( ) to
work there must be a self.mutex created in every class that uses
synchronized( ). This can be created by hand by the class author, but
its more consistent to use inheritance, so the base class
Synchronization is provided.
Add Comment
Heres a simple test of the
Synchronization module.
#: util:TestSynchronization.py from Synchronization import * # To use for a method: class C(Synchronization): def __init__(self): Synchronization.__init__(self) self.data = 1 def m(self): self.data += 1 return self.data m = synchronized(m) def f(self): return 47 def g(self): return 'spam' # So m is synchronized, f and g are not. c = C() # On the class level: class D(C): def __init__(self): C.__init__(self) # You must override an un-synchronized method # in order to synchronize it (just like Java): def f(self): C.f(self) # Synchronize every (defined) method in the class: synchronize(D) d = D() d.f() # Synchronized d.g() # Not synchronized d.m() # Synchronized (in the base class) class E(C): def __init__(self): C.__init__(self) def m(self): C.m(self) def g(self): C.g(self) def f(self): C.f(self) # Only synchronizes m and g. Note that m ends up # being doubly-wrapped in synchronization, which # doesn't hurt anything but is inefficient: synchronize(E, 'm g') e = E() e.f() e.g() e.m() #:~
You must call the base class
constructor for Synchronization, but thats all. In class C
you can see the use of synchronized( ) for m, leaving f and
g alone. Class D has all its methods synchronized en masse,
and class E uses the convenience function to synchronize m and
g. Note that since m ends up being synchronized twice, it will be
entered and left twice for every call, which isnt very desirable [there
may be a fix for this]
Add Comment
#: util:Observer.py # Class support for "observer" pattern. from Synchronization import * class Observer: def update(observable, arg): '''Called when the observed object is modified. You call an Observable object's notifyObservers method to notify all the object's observers of the change.''' pass class Observable(Synchronization): def __init__(self): self.obs = [] self.changed = 0 Synchronization.__init__(self) def addObserver(self, observer): if observer not in self.obs: self.obs.append(observer) def deleteObserver(self, observer): self.obs.remove(observer) def notifyObservers(self, arg = None): '''If 'changed' indicates that this object has changed, notify all its observers, then call clearChanged(). Each observer has its update() called with two arguments: this observable object and the generic 'arg'.''' self.mutex.acquire() try: if not self.changed: return # Make a local copy in case of synchronous # additions of observers: localArray = self.obs[:] self.clearChanged() finally: self.mutex.release() # Updating is not required to be synchronized: for observer in localArray: observer.update(self, arg) def deleteObservers(self): self.obs = [] def setChanged(self): self.changed = 1 def clearChanged(self): self.changed = 0 def hasChanged(self): return self.changed def countObservers(self): return len(self.obs) synchronize(Observable, "addObserver deleteObserver deleteObservers " + "setChanged clearChanged hasChanged " + "countObservers") #:~
Using this library,
here is an example of the observer pattern:
Add Comment
#: c10:ObservedFlower.py # Demonstration of "observer" pattern. import sys sys.path += ['../util'] from Observer import Observer, Observable class Flower: def __init__(self): self.isOpen = 0 self.openNotifier = Flower.OpenNotifier(self) self.closeNotifier= Flower.CloseNotifier(self) def open(self): # Opens its petals self.isOpen = 1 self.openNotifier.notifyObservers() self.closeNotifier.open() def close(self): # Closes its petals self.isOpen = 0 self.closeNotifier.notifyObservers() self.openNotifier.close() def closing(self): return self.closeNotifier class OpenNotifier(Observable): def __init__(self, outer): Observable.__init__(self) self.outer = outer self.alreadyOpen = 0 def notifyObservers(self): if self.outer.isOpen and \ not self.alreadyOpen: self.setChanged() Observable.notifyObservers(self) self.alreadyOpen = 1 def close(self): self.alreadyOpen = 0 class CloseNotifier(Observable): def __init__(self, outer): Observable.__init__(self) self.outer = outer self.alreadyClosed = 0 def notifyObservers(self): if not self.outer.isOpen and \ not self.alreadyClosed: self.setChanged() Observable.notifyObservers(self) self.alreadyClosed = 1 def open(self): alreadyClosed = 0 class Bee: def __init__(self, name): self.name = name self.openObserver = Bee.OpenObserver(self) self.closeObserver = Bee.CloseObserver(self) # An inner class for observing openings: class OpenObserver(Observer): def __init__(self, outer): self.outer = outer def update(self, observable, arg): print "Bee " + self.outer.name + \ "'s breakfast time!" # Another inner class for closings: class CloseObserver(Observer): def __init__(self, outer): self.outer = outer def update(self, observable, arg): print "Bee " + self.outer.name + \ "'s bed time!" class Hummingbird: def __init__(self, name): self.name = name self.openObserver = \ Hummingbird.OpenObserver(self) self.closeObserver = \ Hummingbird.CloseObserver(self) class OpenObserver(Observer): def __init__(self, outer): self.outer = outer def update(self, observable, arg): print "Hummingbird " + self.outer.name + \ "'s breakfast time!" class CloseObserver(Observer): def __init__(self, outer): self.outer = outer def update(self, observable, arg): print "Hummingbird " + self.outer.name + \ "'s bed time!" f = Flower() ba = Bee("Eric") bb = Bee("Eric 0.5") ha = Hummingbird("A") hb = Hummingbird("B") f.openNotifier.addObserver(ha.openObserver) f.openNotifier.addObserver(hb.openObserver) f.openNotifier.addObserver(ba.openObserver) f.openNotifier.addObserver(bb.openObserver) f.closeNotifier.addObserver(ha.closeObserver) f.closeNotifier.addObserver(hb.closeObserver) f.closeNotifier.addObserver(ba.closeObserver) f.closeNotifier.addObserver(bb.closeObserver) # Hummingbird 2 decides to sleep in: f.openNotifier.deleteObserver(hb.openObserver) # A change that interests observers: f.open() f.open() # It's already open, no change. # Bee 1 doesn't want to go to bed: f.closeNotifier.deleteObserver(ba.closeObserver) f.close() f.close() # It's already closed; no change f.openNotifier.deleteObservers() f.open() f.close() #:~
The events of interest are that
a Flower can open or close. Because of the use of the inner class idiom,
both these events can be separately observable phenomena. OpenNotifier
and CloseNotifier both inherit Observable, so they have access to
setChanged( ) and can be handed to anything that needs an
Observable.
Add Comment
The inner class idiom also comes in handy
to define more than one kind of Observer, in Bee and
Hummingbird, since both those classes may want to independently observe
Flower openings and closings. Notice how the inner class idiom provides
something that has most of the benefits of inheritance (the ability to access
the private data in the outer class, for example) without the same
restrictions.
Add Comment
In main( ), you can see one
of the prime benefits of the observer pattern: the ability to change behavior at
run time by dynamically registering and un-registering Observers with
Observables.
Add Comment
If you study the code above youll
see that OpenNotifier and CloseNotifier use the basic
Observable interface. This means that you could inherit other completely
different Observer classes; the only connection the Observers have
with Flowers is the Observer interface.
Add Comment
The following example is similar to the
ColorBoxes example from Chapter 14 in Thinking in Java, 2nd
Edition. Boxes are placed in a grid on the screen and each one is
initialized to a random color. In addition, each box implements the
Observer interface and is registered with an
Observable object. When you click on a box, all of the other boxes are
notified that a change has been made because the Observable object
automatically calls each Observer objects update( )
method. Inside this method, the box checks to see if its adjacent to the
one that was clicked, and if so it changes its color to match the clicked box.
Add Comment
[[ NOTE: this example has not been converted. If youre a TKinter pro, please feel free to do so :-) ]] # c10:BoxObserver.py # Demonstration of Observer pattern using # Java's built-in observer classes. # You must inherit a type of Observable: class BoxObservable(Observable): def notifyObservers(self, Object b): # Otherwise it won't propagate changes: setChanged() super.notifyObservers(b) class BoxObserver(JFrame): Observable notifier = BoxObservable() def __init__(self, int grid): setTitle("Demonstrates Observer pattern") Container cp = getContentPane() cp.setLayout(GridLayout(grid, grid)) for(int x = 0 x < grid x++) for(int y = 0 y < grid y++) cp.add(OCBox(x, y, notifier)) def main(self, String[] args): int grid = 8 if(args.length > 0) grid = Integer.parseInt(args[0]) JFrame f = BoxObserver(grid) f.setSize(500, 400) f.setVisible(1) # JDK 1.3: f.setDefaultCloseOperation(EXIT_ON_CLOSE) # Add a WindowAdapter if you have JDK 1.2 class OCBox(JPanel) implements Observer: Observable notifier int x, y # Locations in grid Color cColor = newColor() static final Color[] colors =: Color.black, Color.blue, Color.cyan, Color.darkGray, Color.gray, Color.green, Color.lightGray, Color.magenta, Color.orange, Color.pink, Color.red, Color.white, Color.yellow static final Color newColor(): return colors[ (int)(Math.random() * colors.length) ] def __init__(self, int x, int y, Observable notifier): self.x = x self.y = y notifier.addObserver(self) self.notifier = notifier addMouseListener(ML()) def paintComponent(self, Graphics g): super.paintComponent(g) g.setColor(cColor) Dimension s = getSize() g.fillRect(0, 0, s.width, s.height) class ML(MouseAdapter): def mousePressed(self, MouseEvent e): notifier.notifyObservers(OCBox.self) def update(self, Observable o, Object arg): OCBox clicked = (OCBox)arg if(nextTo(clicked)): cColor = clicked.cColor repaint() private final boolean nextTo(OCBox b): return Math.abs(x - b.x) <= 1 && Math.abs(y - b.y) <= 1 # :~
When you first look at the online
documentation for Observable, its a bit confusing because it
appears that you can use an ordinary Observable object to manage the
updates. But this doesnt work; try itinside BoxObserver,
create an Observable object instead of a BoxObservable object and
see what happens: nothing. To get an effect, you must inherit from
Observable and somewhere in your derived-class code call
setChanged( ). This is the method that sets
the changed flag, which means that when you call
notifyObservers( ) all of the observers will,
in fact, get notified. In the example above setChanged( ) is simply
called within notifyObservers( ), but you could use any criterion
you want to decide when to call setChanged( ).
Add Comment
BoxObserver contains a single
Observable object called notifier, and every time an OCBox
object is created, it is tied to notifier. In OCBox, whenever you
click the mouse the notifyObservers( ) method is called, passing the
clicked object in as an argument so that all the boxes receiving the message (in
their update( ) method) know who was clicked and can decide whether
to change themselves or not. Using a combination of code in
notifyObservers( ) and update( ) you can work out some
fairly complex schemes.
Add Comment
It might appear that the way the
observers are notified must be frozen at compile time in the
notifyObservers( ) method. However, if you look more closely at the
code above youll see that the only place in BoxObserver or
OCBox where you're aware that youre working with a
BoxObservable is at the point of creation of the Observable
objectfrom then on everything uses the basic Observable
interface. This means that you could inherit other Observable classes and
swap them at run time if you want to change notification behavior then.
Add Comment