Today, in part 4 of my series on OOP programming, I would like to introduce the Observer pattern.

Why Use It?

I think it is absolutely great when I create a class and I find out I can use in
different applications. After all, that's one of the reasons to use Object
Orientation. One of the main problems that prevents a lot of reuse is tight
binding between classes. Tight binding means that one class has to know about
another class in order to function. This is not necessarily a Bad Thing, but it
becomes one as soon as this knowledge of other classes is tied to a single
application.

A good example of what I unfortunately did in the past is binding between
Functionality and the User Interface. When I'm developing something, I like to
see what's going on inside my objects. So I usually make a simple UI that
displays all sorts of information like number of faces/vertices, timing profile,
memory-usage, fps etc. This often resulted in either my functional class (e.g.
3D-engine) having to know about UI-elements, or my UI-class containing
functionality that should really be in the 3D-engine class. Both cases prevent
reuse because the Functionality needs the UI to function properly, and a UI is
very application specific.

The Observer pattern allows an object to let other objects know about its
internal values without knowing anything specific about the classes of these
other objects.

How To Do It

Observer uses inheritance to achieve its goal. The first thing to do is to
create two base-classes, which I dubbed TEventServer and TEventClient (You can
see I use a Borland compiler :) ). These classes look something like this:

As you can see, TEventServer has three methods. Attach() adds a pointer to a
TeventClient to the ClientList. Detach() removes it from this list.
ParameterUpdate() loops through the ClientList and calls the ParameterUpdate
method of all clients in the list. The good bit here is that you can have lots
of clients. So, for example, all the 3D-objects in you world may want to know
about the often changing gravity of the world they reside in. Instead of having
all your 3D-objects poll the "world" for the current value/direction of gravity
on a regular basis, you can let the world call all objects ONLY WHEN NEEDED.
This is clearly advantageous when the number of objects grows very large.

The class containing the functionality of your fancy 3D-engine derives from
TEventServer. The UI displaying all sorts of interesting information about the
engine is derived from TEventClient and implements the ParameterUpdate function.
The UI Attaches itself to the engine by calling Attach(this). In the
ParameterUpdate function of the UI, it finds out about the values of
parameters/variables of the engine it wishes to display and updates its
labels/edit-boxes/statics etc.

How To Improve It

The classes I showed above do the job, but pretty na´ve. For instance, if your
server exposes a hundred variables you have to update all hundred of them in the
UI for every Update. This is because the Clients have no way of knowing which
parameters need to be updated. There are several ways to improve on this. One
way I have seen is to give each parameter a text-name, and pass this name as a
parameter to the Update method. This goes something like this:

The reason to use this is that it gives you pretty readable code: You can see in
the client-code which parameter is changed, and what action this results in,
without looking in the Server-code. I would never use this method, though,
because it's a performance-killer: Passing strings and comparing them is a
relatively slow. In my implementation, I use parameter-groups, which are
identified by a number:

This allows you to decide what granularity you want: You can have all parameters
in one group, or you can have as many groups as parameters

Another possible improvement is to implement the "push"-model. As you can see,
in the implementation I gave, the client (e.g. UI) is responsible for getting
the necessary information it wants to display. The push model means the server
passes information about what changed and what the new values are. There are
several advantages to this method, but there is again a performance penalty
because a lot more data is transferred between functions.

Conclusion

Observer can reduce tight binding of classes, thereby reducing tight binding,
and offers an interesting approach in situations where some kind of
"many-to-one" relationship is desirable. This can go beyond a simple mechanism
to inform others about internal the values of an object. Observer can be the
basis for an elaborate event-driven system, similar to the windows event-system,
where objects attach to multiple servers and respond to a variety of different
kinds of events, ranging from user-input to network-activity.

As always, any feedback is appreciated (Well, as long as it's positive anyway
:-) ).

By the way, on a totally unrelated note: Is anyone interested in setting up a
flipCode Q3A contest/tournament? Would be a lot of fun, I think.

This document may not be reproduced in any way without
explicit permission from the author and flipCode. All Rights Reserved.
Best viewed at a high resolution.
The views expressed in this document are the views of the author and NOT neccesarily of anyone
else associated with flipCode.