Tracking object states - the observer pattern

OcempGUI provides a minimalistic observer pattern implementation in the ocempgui.events module, which enables objects to track changes ('observe') from others. Two classes, the Subject and IObserver, are used for this.

An object, which should expose changes is usually called a subject to which other objects, the observers, subscribe for notification about those changes. To create a subject, the class just needs to inherit from the Subject class, which provides a minimum set of methods and attributes to become observable.

class MyObject (Subject):
    def __init__ (self):
        Subject.__init__ (self, "UniqueSubjectName")
    ...
      
The constructor of the Subject receives a string argument, the name, which identifies the object instance for possible observers.

The object now features all attributes and methods to register observers and to notify them about state changes. State changes however have to be emitted manually, so that the object should invoke its notify() method, whenever this is necessary.

class MyObject (Subject):
    def __init__ (self):
        Subject.__init__ (self, "UniqueSubjectName")
        self._value = None
    
    def set_value (self, value):
        # Preserve old value.
        oldval = self._value

        # Set new value.
        self._value = value

        # Notify observers
        self.notify ('value', old, value)
    ...
      
Whenever set_value() is invoked now, any registered observer will be notified about

An object that shall act as an observer should inherit from the IObserver class and implement its update() method, which will receive the state change notification from the Subject. The signature of the method looks like the following:

class ObserverObject (IObserver):
    ...
    def update (self, subject, prop, oldval, newval):
        ...
      

Note

Your classes do not need to explicitly inherit from IObserver, but have to implement the update() method with its correct signature.

The subject argument is the unique name of the Subject that just changed. prop identifies the detail that changed and oldval and newval contain the old and new value of the detail.

The following example is a complete example based on the excerpts from above. You can find it as python script under examples/observer.py

# Subject/Observer usage example.
from ocempgui.events import Subject, IObserver

# The subject that should notify observers about state changes.
class MyObject (Subject):
    def __init__ (self):
        Subject.__init__ (self, "MyObject")
        self._x = "Simple Attribute"
        self._y = 1234567890
        self._z = None

    def get_x (self):
        return self._x
    
    def set_x (self, value):
        # Preserve old value.
        old = self._x
        self._x = value
        # Notify about change.
        self.notify ("x", old, value)

    def get_y (self):
        return self._y

    def set_y (self, value):
        # Preserve old value.
        old = self._y
        self._y = value
        # Notify about change.
        self.notify ("y", old, value)

    def get_z (self):
        return self._z

    def set_z (self, value):
        # Preserve old value.
        old = self._z
        self._z = value
        # Notify about change.
        self.notify ("z", old, value)

    x = property (get_x, set_x)
    y = property (get_y, set_y)
    z = property (get_z, set_z)

class OwnObserver (IObserver):
    def __init__ (self):
        pass

    def update (self, subject, prop, oldval, newval):
        if subject == "MyObject": # A MyObject instance, check details.
            if prop == "x":
                # Its x value changed.
                print "The x value of a MyObject instance changed from " \
                      "%s to %s" % (str (oldval), str (newval))
            elif prop == "y":
                # Its y value changed.
                print "The y value of a MyObject instance changed from " \
                      "%s to %s" % (str (oldval), str (newval))
            else:
                # Another value changed.
                print "The %s value of a MyObject instance changed from" \
                      "%s to %s" % (str (prop), str (oldval), str (newval))

class AnotherObserver (IObserver):
    def __init__ (self):
        pass

    def update (self, subject, prop, oldval, newval):
        print "Detail %s of %s changed from %s to %s" % (str (prop), subject,
                                                         str (oldval),
                                                         str (newval))

subject = MyObject ()

# Add tow observers doing
observer1 = OwnObserver ()
observer2 = AnotherObserver ()

subject.add (observer1, observer2)

subject.x = "FooBarBaz"
subject.y = subject.x * 3
subject.z = 100

Example 11. Observer example