Channels ▼
RSS

.NET

Generalizing Observer


The function facility, recently adopted by the C++ standards committee, provides a generalized way of working with arbitrary functions when all you know (or need to know) is their approximate signature. It turns out that this facility enables us to write, among other things, generalized implementations of important design patterns like Observer. Those generalized design patterns, in turn, motivate two small but important extensions to function itself. [Note: In both the two previous columns, I promised to talk about inline in "the next column." Last time I put off inline in order to cover another fun topic, namely private. This time I've again decided to put off inline because I had so much to say about function in my current The New C++ column [1] that it turned into two columns - one to describe function itself (which was the topic of [1]), and one to describe a generalized Observer implementation that uses function and motivates changes and extensions to function (this article). I'm going to stop making promises about inline, but I really do intend to treat it once I stop thinking of even more interesting things to write about.]

In [1], I gave an overview of one of the first two extensions that were approved in October 2002 for the standard library extensions technical report (the "Library TR"): Doug Gregor's proposal for polymorphic function object wrappers. [2] This function facility comes directly from the Boost project [3], an important (and free) collection of C++ libraries available for you to download and try out today.

In brief, the function facility provides a generalized way of working with arbitrary functions when all you know (or need to know) is their signature. In fact, as it turns out, you don't even need to know the target function's exact signature - any target function with a compatible signature, meaning one where the parameter and return types are appropriately convertible, will work just fine. A function can bind to a nonmember function, a member function, or a functor that defines its own operator() and can therefore be called just like a function. (I'm going to use the term "functors" for the latter, instead of the alternative "function objects," to avoid confusion with "function objects" which means objects of the function library facility we're discussing.)

In this article, I'll focus on showing how function lets us simplify and expand the classic Observer pattern. Along the way, I hope to persuade you that Observer (and other patterns like it) compellingly demonstrates not only the usefulness of function, but also why function itself should be further generalized beyond its current state in the draft standard library technical report.

A Motivating Example for function: The Observer Pattern (or, Beyond Singlecast)

Briefly, the Observer pattern "define[s] a one-to-many dependency between objects so that when one object [the subject] changes state, all its dependents [its observers] are notified and updated automatically." [4] Observer commonly arises in event-driven systems, such as operating systems and GUI environments (e.g., Smalltalk's Model/View/Controller framework), where objects or processes need to respond to state changes elsewhere in the system (e.g., external input) without knowing in advance when they might happen. Allowing such observers to idle and only respond to events as they occur is almost always better than requiring them to perform constant polling, or busy-waiting, just in case an event might have happened since the last time the observers looked in on their subject.

As described in [4] in an object-oriented form, a Subject lets observers register themselves for later callback. The observers inherit from a common base class, and the subject keeps a list of base class pointers. Figure 1 shows the structure of this object-oriented form of Observer:

Figure 1: Observer pattern's structure, as it appeared in Design Patterns [4]


The code might look something like the following Example 2 (adapting the example in [4]). I've kept Subject's member functions as virtual for consistency with [4], even though it's debatable whether or not that is a generally useful choice.

// Example 2: A sample OO Observer implementation
//
class Subject;

class Observer {
  // ...
public:
  virtual void OnUpdateOf( Subject* ) = 0;
};

class Subject {
  // ...
public:
  virtual void Attach( Observer* o ) { obs_.insert( o ); }
  virtual void Detach( Observer* o ) { obs_.erase( o ); }
  virtual void Notify() {
    for( set<Observer*>::iterator i = obs_.begin(); i != obs_.end(); ++i )
      (*i)->OnUpdateOf( this );
  }
private:
  set<Observer*> obs_;
};

Here's an example use, following [4]:

class ClockTimer : public Subject {
  // ...
  void Tick() {
    // ... timekeeping logic...
    Notify();	// every so often, tick
  }
};

class DigitalClock : /*...*/ public Observer {
  // ...
public:
  DigitalClock( ClockTimer* t ) : timer_(t) { timer_->Attach( this ); }
  ~DigitalClock() { timer_->Detach( this ); }
  void OnUpdateOf( Subject* timer ) { /* query timer and redraw */ }
private:
  ClockTimer* timer_;
};

Note that in this OO version of the pattern, Subjecthas two limitations: First, it is hardwired to be observable only by an object of a type that inherits from Observer. Second, the callback function itself has to have a prescribed name and exact signature. Both of these limitations arise because Subject stores its observers list using object pointers into a fixed hierarchy.

How can we remove these limitations? As a first step, we might think we could alleviate the second limitation somewhat by storing a member function pointer, which would theoretically allow the callback function to have any name, although it would still have to have a fixed signature. There are several problems with this approach: First, for the callback functions to be useful they must still be predeclared as pure virtuals in the Observer base class, which means that although the callback gains some name flexibility, it still has to be one of a predefined set of special known names (reminiscent of Henry Ford's famous quote, "you can have any color you want, as long as it's black"). Second, the Subject would have to store the member function pointer not instead of, but in addition to, the object pointer, so that it knows what object to dispatch to.

Fortunately, there is a better way.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video