Saturday, September 27, 2008

Observer pattern for Python



The Observer pattern is mainly used to implement a distributed event handling system. The primary objective of this pattern is to provide a way to handle run-time one-to-many relationships between objects in a loosely coupled arrangement.

In this configuration, the Observable object doesn't know anything more about it's Observers than a very limited interface. The Observable needs to provide a wide interface for allowing other objects to gain access to it's current state.

The event from the observable object's point of view is called notification and the event from the observers' point of view is called update.


class Observable( object ):
def __init__( self, *args, **kwargs ):
super( Observable, self ).__init__( *args, **kwargs )
self.__dirty = False
self.__observers = weakref.WeakKeyDictionary( )

def attach_observer( self, obs ):
if obs not in self.__observers:
self.__observers[obs] = 1
return self

def detach_observer( self, obs ):
if obs in self.__observers:
del self.__observers[obs]
return self

def set_dirty( self, d ):
self.__dirty = d
return self.__dirty

def is_dirty( self ):
return self.__dirty

def notify_all( self ):
for observer in self.__observers.keys( ):
observer.observer_update( self )

def notify_check( self ):
if self.is_dirty( ):
self.notify_all( )
self.set_dirty( False )
attach_observer and detach_observer maintain the list of Observers that are interested in this object. After any change in state, notify_all should be called. If this state change is part of a larger transaction, the combination set_dirty and notify_check should be called.

If you're also using Stackless python, you may want to have notify_all use the event-loop mechanism we've previously discussed.

class Observer( object ):
def __init__(self, *args, **kwargs ):
pass

def observer_update( self, object ):
pass
The Observer object is very easy to implement. Really, only it needs observer_udpate defined since that method is called by Observable during notify_all. The observed object passes itself as the argument to observable_update so that the observer knows which of the objects it currently is observing has been updated.

No comments: