<!-- Main HTML starts here -->
In general, patterns are not only applicable to software systems and not restricted only to software field. When we look things from a "pattern perspective", we can feel how patterns are integral part of our life and day-to-day activities. To quote some examples, doctors follow a specific pattern (or path) in diagnosing medical problems; architects use patterns in constructing buildings and chefs use patterns in cooking variants of a dish. Whatever the field is, the intent of a pattern is to provide solution to a recurring problem without having to reinvent the solution again and again. As a matter of fact, Christopher Alexander used patterns in the constructing of buildings and towns, which served as an inspiration for Erich Gamma et al to come up with the first authoritative work on Object Oriented Design Patterns.
It is a general feeling that Object Orientated concepts including design patterns are complex to understand. This is true at least with people who are new to object orientation and design patterns. Examples used to explain abstract concepts should be very clear for easy understanding; software examples are not always suitable for explaining certain abstract concepts. I strongly feel that any abstract concept can be better explained by relating them to concrete real world examples. Interestingly, we could relate software design patterns to activities that are integral part of our life, like our job, for example. In this article, I would like to introduce a design pattern called Chain of Responsibility and show how it can be related to the functioning of a real world Customer Support System. Throughout this article, I will explain the concept of Chain of Responsibility by relating them to real world and software world examples side by side.
I am working in a team providing client support for software products. Like most support models, the overall support responsibility for our products is divided among different teams. To provide a 24x7 support, we have out-sourced the first level of support to a reputed vendor. My team is responsible for providing the second level support, while the development team back us up to provide third level support when needed. Client usually calls the first level support team with the problem request. Support person collects the request details, assigns a request number and records the request onto a tracking system. Then the support person uses the knowledge gained or uses the knowledge base to handle the request. If the request is successfully handled it is closed or else it is escalated to my team. My team tries to handle the request or escalate it to the development team to provide a quick response and close the problem.
Typically, a client request flows through a chain of people until it is successfully handled. The support model clearly defines the handling procedures and the escalation paths, in case the request needs to be forwarded. Functioning of the support team could be related to chain of objects co-operating to handle a request. Clients initiating a request with the first level support team will receive a response from one of the teams (not always from the first level support team) without having to know about the whole support model. Likewise, a client object can initiate a request to an handler object, which can provide a response or chain the request to the next handler object which can provide a response or in turn chain the request to the next handler object. Ultimately, the client getting the response need not know anything about the object handling the request. This object behavioral pattern is called, Chain of Responsibility. The main intent of this pattern is to de-couple the requestor (or the client object) from the actual handler (or the responding object) by giving an opportunity for more than one object to handle a given request. Both in the real world and object world, Chain of Responsibility allows handlers to be chained and pass the request along the chain.
Chain of Responsibility in MFC Message Handling
Main focus of this article is to show the use of Chain of Responsibility
in our support system (real world) and MFC's message handling (software world), so MFC Message Maps and
implementation details are beyond the scope of this article. MFC Internals by George Shepherd and Scot Wingo has a separate chapter on Message handling in MFC.
Windows is an event driven operating system. When a user interacts with a Windows application, it generates an event or a command message to the active window object. In traditional Windows programming, a windows procedure in the window object will have to handle the command. This leaves with just one handler for all the requests generated by user interaction. MFC framework has extended message-handling capabilities to other non-window objects like Documents (
CDocument) and Application (
CWinApp) objects. The main reason is to provide modularity and ensure separation of interests. For example, it is more appropriate for a document object to handle commands related to creating, loading and saving documents; view object to handle user interaction commands like editing and copying; while leaving application object to handle application level commands like help and exit. MFC refers to these objects as Command Targets, as they all can handle user commands. As there is more than one command target, MFC employs Chain of Responsibility pattern to define a protocol for handling or routing commands. This is very similar to our support system, where there is more than one team (command target) to handle client requests (commands) and our support model defines the protocol for handling and routing. I have used the terms requests and commands; handlers and command targets interchangeably in the following paragraphs.
Before getting into further details, we will see the three participants Handler, Concrete Handler and Client involved in Chain of Responsibility. Handler defines an interface for handling requests and optionally maintains a link to next handler in the chain; Concrete Handler is responsible for handling requests if it can or forwards the request to the next handler; Client initiates the request to a Concrete Handler in the chain.
In our support system, the overall support model (or Handler) defines the interface or protocol for handling requests; Concrete handlers are different support teams and Client is our customer calling the first level support. In MFC,
CCmdTarget is the Handler and
OnCmdMsg member function is the interface for handling requests.
CDocument are Concrete Handlers. Menus, Toolbars and Accelerator Keys are Clients initiating the command (request) to the main frame window.
In our support system, knowledge base and run book are used for handling the request and escalation paths define rules for routing the request to the next team when needed. In MFC, Command targets use
OnCmdMsg member function to see whether they can handle a request or to route them to the next command target. The default implementation of
CCmdTarget uses the message map of the command target class to search for a handler function for each command it receives. If a match is found, then the handler is called. For command routing, command target calls the
OnCmdMsg member function of the next command target in sequence. MFC uses a standard command routing table for determining the next command target. This table is present as part of Microsoft Visual C++ on line help and a copy of which is given at the end of this article for reference.
Client requests are recorded in our tracking system, which is used for routing requests between different teams or handlers. In MFC, requests are passed on to different command targets through the parameters of
OnCmdMsg member function.
In our support system, the request recorded in the tracking system is closed when it is handled; similarly
OnCmdMsg returns TRUE when the request is handled successfully or FALSE to pass the request to the next handler. Passing the request to the next handler or linking command targets in MFC is easy because of the object relationship that exists among MFC classes. Frames window knows about the active view, view knows about the document, document knows the document template etc. In MFC, command target routes the commands to the active child command target object (if present). If that object cannot handle the request, then command target itself tries to handle it, if not it routes the request to other command targets that it knows. This is very similar to the well-defined escalation paths in our support model.
Benefits and DrawbacksReduced coupling
- Both the handler and the client have no explicit knowledge of each other, and an handler in the chain doesn't have to know about the chain's structure. A support system client and MFC client know how to reach the first handler in the chain and need not know anything about the chain structure. Coupling between handlers is greatly reduced when the means of passing the request among handlers (through tracking system for support and
method parameters in MFC) and the route for passing the request (escalation routes defined as part of the support model and command routing table in MFC) is clearly defined.
Added flexibility in assigning responsibilities to objects - Chain of Responsibility gives an added flexibility in distributing responsibilities among handler objects. In our support system, when the first level support team is fully occupied with customer requests, new requests are automatically routed to my team. Also, based on the volume of calls we have the flexibility of having additional support teams on a temporary or permanent basis without disrupting the chain. Similarly, MFC framework offers the flexibility of distributing commands to any of the command targets (statically or dynamically). For example, user command can be handled by active view (usually) or by frame window and this can be decided at compile time or runtime.
Receipt is not guaranteed - As there is no explicit receiver for the request, there is no guarantee it will be handled and it may fall off the end of the chain without ever being handled. However, this can be avoided by putting some controls in place during implementation. In our support system, the support manager will ensure that his team handles the request or escalates it to the next team in order to guarantee a timely response to the client. Similarly, MFC ensures that menu items, toolbar buttons and accelerator keys are disabled when there is no handler for a command.
When to use Chain of Responsibility?
Clearly, Chain of Responsibility
can be used when:
- More than one object may handle a request, and the handler is not known a priori;
- When a request has to be issued to several objects without specifying the receiver explicitly;
- The set of objects that can handle a request should be specified dynamically
These uses can be related easily to the examples and explanation given in Benefits and Drawbacks section.
Abstract concepts can be better explained with real world examples. This article introduced a design pattern called the Chain of Responsibility
with examples from a real world support system and a software world MFC message handling mechanism. In general, Chain of Responsibility
can be used to decouple the requestor and the handler, especially when there is more than one handler and when the handler is not known a priori.
- Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides: "Design Patterns - Elements of Reusable Object-Oriented Software", Addison Wesley
- George Shepherd and Scot Wingo: "MFC Internals, Inside the Microsoft Foundation Class Architecture", Addison Wesley
Standard Command Routing table taken as is from Microsoft Visual C++ online help is given below
<TABLE borderColor=#808080 cellSpacing=1 cellPadding=3 width="100%" border=1
When an object of this type receives a command . . .
It gives itself and other command-target objects a chance to handle the command in this order:
MDI frame window
- This frame window
- Application (
Document frame window
- Active view
- This frame window
- Application (
- This view
- Document attached to the view
- This document
- Document template attached to the document
- This dialog box
- Window that owns the dialog box
- Application (