PATTERNS AND SOFTWARE DESIGN

The Courier Pattern

Richard Helm and Erich Gamma

Richard is an architect with the IBM Consulting Group's Object Technology Practice in Sydney, Australia. Erich is an architect and object technologist at IFA in Zurich, Switzerland. They are coauthors of Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley, 1994).


In previous columns, we've stressed the importance of ensuring that objects that send a request assume only that objects receiving those requests support a particular interface, but not anything about the receiving objects' concrete class (that is, its implementation). This ensures that senders are decoupled from receivers, and results in a characteristic design where the definition of operations refers only to abstract--not concrete--classes. Several design patterns from our book Design Patterns: Elements of Reusable Object-Oriented Software involve decoupling senders and receivers of requests; the Observer pattern discussed in our "Patterns and Software Design" column (Dr. Dobb's Sourcebook, September/October 1995) is one of them.

As Figure 1 illustrates, the Observer pattern decouples Subjects from their dependent Observers by defining an interface for signaling changes in the Subject. When decoupling senders and receivers, a common design issue is the kind of information passed between them. The simplest solution is to pass no information at all. Listing One implements a Notify() operation that doesn't pass any information. Since no information is passed to the Observer and its Update operation, the Observer has to find out what changed by asking the Subject for its current state. In other words, it has to "pull" the state from the subject. When it is expensive for the Observer to determine what changed, the pull approach can be also be expensive. Clearly, there needs to be a way for the Subject to pass more-specific change information. The challenge, then, is making a concrete subject pass specific information about what changed to its concrete observers, given that the interface between subjects and observers is defined by the abstract classes Subject and Observer.

One way to solve this is by adding a void* as a parameter to a tag that identifies the information passed in the void*. For example, you could change the Update operation of Observer to Update(longwhatChanged, void* info). This approach is not particularly type safe, and a void* in a higher-level class interface is not particularly elegant.

Chain of Responsibility is another pattern that decouples the sender from the receiver. It does this by passing a request along a chain of potential receivers. As Figure 2 suggests, event handlers are a good use of this pattern. With the Chain of Responsibility pattern, objects at the front of the chain try to handle requests (events) generated by some initial sender. If they cannot, they pass the request along to the next object in the chain, hoping that some object down the line will eventually be able to handle it. Chain of Responsibility requires that each object in the chain have an interface for handling the request. Again, the problem is, how can a sender pass arbitrary information to its candidate receivers, or how can new types of events be handled by the chain of objects? Defining a fixed interface for a fixed set of events such as HandleMouseEvent or HandleTimerEvent is not a viable solution when the set of events is extensible. For example, if the system were extended to support drag-and-drop events, you would have to change existing classes and add a HandleDropEvent operation.

The Mediator pattern also decouples objects by having them refer to each other indirectly through a Mediator object (see Figure 3). The Mediator object is responsible for routing requests between Colleague objects. In the Mediator pattern, Colleague objects do not send requests directly to each other but instead go through an intermediary Mediator. This nicely decouples Colleagues from each other, but also means that Colleagues can only communicate with each other via the fixed Mediator interface. Again, you have the problem of defining an interface that enables concrete colleagues to pass specific information to one another.

To summarize, decoupling objects by defining them in terms of interfaces defined in abstract classes is often the basis for reusable, object-oriented designs. But the objects may become so decoupled from each other that their interfaces do not allow for the passage of specific information. The Courier pattern is one solution to this problem.

The Courier Pattern

Courier patterns allow objects to pass arbitrary requests and information through a fixed interface. The key to the Courier pattern is to package the information to be sent between objects as an object itself and pass this "message object" as an argument to requests.

Let's begin by exploring the Courier pattern in the context of the Observer pattern. Assume that a TextSubject class defines an editable text buffer, and a TextObserver somehow displays the TextSubject (Figure 1). The TextObserver class receives notifications about changes in its TextSubject. Implementing a TextObserver using the interface defined for the Observer class in Figure 1 would require that, when the TextSubject changes in any way, the TextObserver has to retrieve the complete text buffer from the TextSubject. There is no way for the TextObserver to determine what changed in the TextSubject using just the Update() interface. Ideally, the TextSubject would be able to inform its Observer exactly what about itself changed. Using the Courier pattern, we would package this information into an object and send that to the TextObserver.

We first define a message object using the Notification class. In this case, we define it as an empty class; see Listing Two. Then we change the Notify and Update interfaces of Subject and Observer; see Listing Three.

A TextSubject that wants to pass additional information to its Observers can now define a TextChangeNotification subclass that stores additional information about the changed text range. Assume in Listing Four that there is already a TextRange class that can be used to specify a range of changed text. Now the TextSubject can pass a TextChangeNotification to notify its observers; see Listing Five. Finally, a TextObserver can use this additional change information to optimize how it updates itself; see Listing Six.

Other kinds of notification requests (text deletion, style changes) could be easily added and handled in a similar way.

In the Courier pattern, notification requests are represented as objects. The recipient of the request must analyze the message object and handle it appropriately. First, the notification object has to be decoded to identify its concrete class (we did this using the run-time, type-identification mechanism provided by C++). Then the appropriate code to handle this request must be determined and executed. In some situations, if the recipient class cannot handle the request, it may have to defer it to its parent classes. This adds run-time overhead.

Participants

As Figure 4 shows, the key participants in the Courier pattern are:

Note that in Figure 4, only the Sender and the ConcreteRecipient know about the ConcreteMessage.

Applicability

When should you use the Courier pattern? The first situation is when a fixed interface defined between abstract classes is insufficient to pass all information required to recipients that are concrete subclasses of these abstract classes.

A second situation is when the requests to be sent between objects cannot be anticipated in advance, and you need to provide hooks to allow the interface to be extended for new requests. This occurs in event handlers implemented using Chain of Responsibility when you want to extend the kinds of events the handlers can process.

In the third situation, the class and the interface of the receiver are unknown to classes sending requests, because the requests are sent via a third object. In Mediator and Chain of Responsibility, the class and even the interface of the class of object receiving requests are unknown to the sender. Rather than having the intervening object modifying or adapting the requests passing through it so it can be received by the ultimate recipient, we instead define a single Handle(Message&) interface to allow the Messages to be passed to the final Recipient unchanged.

Most uses of Courier are for more-loosely decoupling classes than is possible through interfaces defined by abstract classes. Another situation in which Courier can be very useful is during initial system prototyping and development.

One problem with statically typed languages (such as C++) is that you must explicitly specify the interfaces to abstract classes in their declaration. You also have to duplicate much of this interface declaration (certainly for the virtual functions) in all subclasses inheriting from these abstract classes. Yet, as systems evolve, their interfaces change. This is especially true of newer systems, where it usually takes a few design iterations to find the major abstractions and specify their interfaces correctly. Changing interfaces to classes in C++ can be a costly operation. Not only do all subclass declarations and definitions need to be rewritten, but a significant amount of compile time may be spent in all clients implemented in terms of this interface.

For systems being prototyped or evolving rapidly, an alternative is to use the Courier pattern to send requests between classes in a system. Because Courier allows interfaces of classes to be changed just by modifying class implementations (or more precisely, the dispatching code in their Handle(Message&) operations), there is much less cost in changing interfaces. You simply have to add new Message subclasses and the appropriate dispatching code.

When the interfaces eventually begin to "harden," the implicit dispatching provided by the Courier pattern can be replaced by direct requests to operations defined explicitly by the classes. The bodies of these operations will be what was previously the bodies of the If statements in the Recipient's dispatching operations.

Advantages and Disadvantages

The Courier pattern offers a number of benefits:

The Courier pattern also has some inherent disadvantages:

Implementation Issues

One of the challenges when implementing Courier patterns is identifying the Message classes. To be able to handle a Message, a Recipient must be able to identify the kind of Message it receives. The Message can define an accessor function that returns an identifier for the class as a constant or a string. This requires that the sender and receiver agree on the encoding. Alternatively, the receiver can use the language-supported, run-time type information, as we have in the Observer example. In this case, there is no need for special Message encoding conventions.

A second issue is acknowledging Message receipt. Senders sometimes need to know whether a Message was handled or not. For example, when propagating a message along a chain of responsibility, the sender can stop propagating the request once it is handled. To provide this kind of information, the Handle(Message&) operation can return a Boolean.

Another implementation issue involves handling requests, which requires identifying the message, then performing the appropriate action. Message handling is often distributed across a base recipient class and its subclasses. The base class can handle a more-general message, and the subclasses can handle more-specific messages. To enable this kind of message handling the derived class has to invoke the inherited Handle operation when it receives a message that it does not handle, as in Listing Seven.

The Recipient's message dispatch code is essentially boilerplate code. Where possible, the burden of having to create this boilerplate should be removed from the client. One way is to have a code generator (often called a "wizard") that generates the code based on some specification.

Self-Dispatching Messages

One potential problem with implementing Courier patterns is that all the dispatching code is in the Recipient. An alternative solution is to make Messages self dispatching.

When we perform the dispatching, we have different Recipients and different Messages. Code that actually gets executed when a message is received depends on both the concrete classes (more strictly speaking, the type) of both the Recipient and Message objects. There are languages (CLOS, for example) that support operations (multi-methods-methods) that can be dispatched on more than one parameter type. In such languages, message dispatching would be handled by language directly and not implemented programmatically by the programmer. However, languages such as C++ and Smalltalk dispatch operations on the type of only one object--the object receiving the request.

One technique to dispatch operations on multiple classes in such languages is "double dispatch." We use a variation of this technique in Listing Eight. (The only implementation we're aware of that uses this technique is the Input System of the Taligent Frameworks.)

The Messages dispatch themselves. To do so, the base Message class defines a Dispatch function that takes a Handler as a parameter. The Handler class itself has the interface in Listing Eight. The Message base class implements the Dispatch function by simply calling HandleMessage on the handler passed to it; see Listing Nine. A Sender uses these classes in Listing Ten. So far this appears not very useful, as we've only introduced an additional level of dispatching. But Sender need know nothing about the concrete classes of the Message or the MessageHandler.

Next, we define abstract classes that specify an interface for classes that wish to handle particular kinds of messages. Let's take a TimerMessage class defined as in Listing Eleven. For a TimerMessage, we define the TimerMessageHandler class in Listing Twelve. These handler classes for specific messages are used as mixin classes. A class that wants to handle a TimerMessage has to inherit from the TimerMessageHandler class and implement the pure virtual HandleTimerMessage function; see Listing Thirteen. Since the Sender sees only Messages and MessageHandlers, you may wonder how a particular Message finds its way to the specific MessageHandler. We first request a message to dispatch itself to a particular handler, then the message requests the handler to handle it. Consider the implementation of Dispatch for TimerMessage. It uses run-time type information to check whether the handler is a TimerMessageHandler. If it is, the message is delivered as a TimerMessage by calling HandleTimerMessage; see Listing Fourteen. Dispatch invokes its inherited operation that delivers the Message as an ordinary message. The handler can use the less efficient, but more general, HandleMessage operation to handle the message.

The Sender's first dispatch shows that the message is in fact a TimerMessage. The message then works out the type of the handler to call the appropriate operation for it, in this case HandleTimerMessage.

This technique frees the client code from dispatching messages by delegating the dispatching logic to the Message itself. However, it does have some drawbacks:

Figure 1: Observer pattern decouples Subjects from their dependent Observers.

Figure 2: Using the Chain of Responsibility pattern for event handlers.

Figure 3: The Mediator pattern; (a) with no Mediator; (b) with Mediator.

Figure 4: Key participants in the Courier pattern.

Listing One

class Observer {
public: 
    //...
    virtual void Update();
};
class Subject {
public:
    void Attach(Observer*);
    void Detach(Observer*);
    void Notify();
private:
    List<Observer*> _observers;
};
void Subject::Notify ()
{
    ListIterator<Observer*> i(_observers);
    for (i.First(); !i.IsDone(); i.Next() ) {
        i.CurrentItem()->Update();
    }
}

Listing Two

class Notification {
public:
    virtual ~Notification();
protected:
    Notification();
};

Listing Three

class Subject {
public:
    // ...
    void Notify(Notification&);
};
class Observer {
public:
    // ...
    void Update(Notification&);
};

Listing Four

class TextChangeNotification: public Notification {
public:
    TextNotification(TextRange range);
    TextRange GetRange() const;
    //...
private:
    TextRange _range;
};

Listing Five

void TextSubject::ReplaceText (TextRange range)
{
    // Change the Text...
    // ... and tell all Observers
    Notify(TextChangeNotification(range));
}

Listing Six

void TextObserver::Update(Notification& notification)
{
    TextChangeNotification *changeNotification;
    changeNotification =        
        dynamic_cast<TextChangeNotification*>(&notification);
    if (changeNotification) {
        TextRange range = changeNotification->GetRange()
        // do an incremental update using _text->GetText(range);
    } else {
        // do a full update using _text->GetText();
    }
}

Listing Seven

void Recipient::Handle(Message& m)
{
    MyMessage *myMessage = dynamic_cast<MyMessage*>(&m);
    if (myMessage) {
        HandleMyMessage(myMessage);
        return;
    }
    OtherMessage *otherMessage = dynamic_cast<OtherMessage*>(&m);
    if (otherMessage) {
        HandleOtherMessage(otherMessage);
        return;
    }
    // unknown message, pass it to the base class BaseHandler
    BaseRecipient::Handle(m);
}

Listing Eight

class MessageHandler {
public:
    virtual void HandleMessage(Message& message);
    //...
};

Listing Nine

class Message {
public:
    virtual void Dispatch(MessageHandler& handler);
    //...
};
void Message::Dispatch(MessageHandler& handler)
{
    handler.HandleMessage(*this);
}

Listing Ten

void Sender::Operation() {
    Message *aMessage;
    MessageHandler *aHandler;
    // .. dispatch the message...
    aMessage->Dispatch(aHandler);
}

Listing Eleven

class TimerMessage: public Message {
public:
    TimerMessage(long);
    int GetTime() const;
    virtual void Dispatch(MessageHandler& handler);
    //...
};

Listing Twelve

class TimerMessageHandler : public MessageHandler {
public:
    virtual HandleTimerMessage(TimerMessage& message) = 0;
};

Listing Thirteen

class MyHandler: 
    public SomeBaseClass, 
    public TimerMessageHandler {
public:
    virtual void HandleTimeMessage(TimerMessage& message);
    //...
};

Listing Fourteen

void TimerMessage::Dispatch(MessageHandler& handler)
{
    TimerMessageHandler* timerHandler;
    timerHandler = dynamic_cast<TimerMesageHandler>(&handler);
    if (timerHandler)
        timerHandler->HandleTimerMessage(*this);
    else
        Message::Dispatch(handler);
}
End Listings