In my previous Exploring OO Design Patterns: The Strategy Pattern, we considered how the Strategy pattern could be used to dynamically select the appropriate solution to a problem at runtime. Obviously, one of the prerequisites for implementing this pattern is having pre-existing knowledge about the types of problems that could arise and their corresponding solutions. However, sometimes we don’t know how to solve a problem until we examine it more closely. In this blog, we’ll explore the use of the Chain-of-Command design pattern and show you how it can be used to implement solutions to these kinds of problems.
The Chain-of-Responsibilty Pattern: Defined
The Chain-of-Responsibility pattern is often used to handle various types of requests or events. Here, whenever an incoming request is received, we may not know how to handle it until we examine the details of the request at length. Rather than defining one big monolithic request handler module, we would prefer to keep the various handler solutions separate so that they can vary independently. Then, we can weave the individual request handlers into a composite solution by chaining them together. In this way, an incoming request is forwarded along the chain until a request handler is found that can process the request. Normally, the process stops after the request handler is found. On the other hand, in some cases, we might want to let the request continue down the chain so that each handler module can add additional functionality to the request handling process. The figure below demonstrates how a request is processed by a chain of request handlers.
Now that you have a feel for how the Chain-of-Responsibility pattern works, let’s look at a more formalized definition. In the Gang of Four book, the Chain-of-Responsibility pattern is defined as follows:
Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.
In order to be able to implement a generic chain of modules, each module must conform to the same interface. The UML class diagram below demonstrates how this works with a generic handler interface called Handler. Each of the modules in the chain implement this interface, providing module-specific functionality in the overridden handleRequest() method. At runtime, each of these modules can be invoked polymorphically.
Case Study: Implementing Handler Lists in the ICF
Sometimes, abstract concepts like design patterns are best explained with an example. If you’ve had an opportunity to work with some of the Web-based technologies of the AS ABAP such as BSPs, Web Dynpro, etc., then you are probably somewhat familiar with the Internet Communication Framework (ICF). The ICF is an object-oriented framework that simplifies the way that you interact with the module that puts the Web in the SAP Web AS: the Internet Communication Manager (ICM). Whenever you host Web applications on the AS ABAP, the entry point into these applications is realized in the form of an ICF service node. ICF service nodes define basic information about a Web application such as the request path, authentication details, etc. In addition, ICF service nodes also allow you to configure a handler list. This handler list implements the core functionality of the Web application, allowing you to respond to HTTP requests programmatically.
To put all this into perspective, let’s consider the ICF service node that implements the BSP runtime environment. ICF service nodes are maintained in Transaction SICF. The figure below shows the initial screen of this transaction. Here, you can click on the Execute button to maintain service nodes.
On the subsequent maintenance screen, you can see that ICF service nodes are organized hierarchically underneath virtual hosts. These hosts represent the Web host that is hosting the Web application. As you can see in the figure below, the BSP runtime environment is nested underneath the default host at the following path: sap –> bc –> bsp.
To view/edit the bsp service node, simply double-click it. The figure below shows the handler list for the bsp service node. Here, a single handler class called CL_HTTP_EXT_BSP has been configured. This class (and all ICF handler nodes) implements the IF_HTTP_EXTENSION interface.
The UML class diagram below shows how the IF_HTTP_EXTENSION interface is defined. As you can see, this interface is defined in much the same way as the Handler interface illustrated earlier. Here, concrete subclasses can implement the proper request handling functionality in the HANDLE_REQUEST() method. This method has access to the details of the request, as well as a reference to an object that can be used to generate the appropriate response. In addition, the IF_HTTP_EXTENSION interface also defines a public instance attribute called FLOW_RC. This attribute allows a handler module to pass back a return code to the surrounding ICF framework. Based upon the value of this return code, the framework can determine whether or not to call the next handler module in the handler list.
As I mentioned earlier, it is sometimes useful to allow a request to continue down the chain so that other handler modules can process the request further. For example, imagine that you have a custom logging requirement for any incoming BSP request. Here, rather than implement that logging requirement in each BSP application, you could interject a handler module before the CL_HTTP_EXT_BSP handler in the handler list so that you could implement the logging requirement centrally. In this case, you don’t want the custom module to take the place of the BSP handler, you just want to implement some value-add functionality ahead of it. These same concepts also apply to SOAP toolkits that may need to implement special SOAP header processing, etc.
Hopefully by now you have a feel for how to implement the Chain-of-Responsibility pattern in your own development projects. In my next blog, we’ll look at another way of implementing this kind of behavior using the Decorator pattern.