<

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x

Chain of Responsibility - ReExplained

Published on
3,673 Points
673 Views
Last Modified:

Introduction


This article discusses the Chain of Responsibility pattern, explaining

  • What it is;
  • Why it is; and
  • How it is
At the end of this article, I hope you will be able to describe the use and benefits of Chain of Responsibility.
 

Background

In your programming career, you might have faced the scenario where you have multiple commands or requests and each command must be processed in some specific way. For that we create handler objects that process those commands. We create those command handlers in a one to one mapping fashion, i.e. one handler for one command or request.
 
Generally, handlers are selected by the type of request, and this is done by a method that might handle the selection via an if/then/else statement, though in some cases typeOf or type checking is used. The problem is with the "if" condition. This kind of common problem can be resolved easily by designing an object hierarchy based on good object-oriented design principles. One of the best applicable patterns is described by the Gang of Four as "Chain of Responsibility".

  

What is it?


The Chain of Responsibility is a behavioural pattern, as it is used to manage algorithms, relationships and responsibilities between objects. According to GoF, the formal definition is


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.

This pattern puts multiple command handler objects into the chain. Each command handler contains the reference of the next object in the chain, so basically it creates a linklist of handlers. The request or command is passed along this chain of objects; each object checks the request to determine whether it can process the request or not. If current object can't process the request, it simply ignores the request and pushed the original request to next object in chain, thus giving another object the opportunity to process the request. If the current object is capable of processing the request then it processes the request. Once the processing is done, the current object can either exit the chain or pass the original request to the next handler in the chain for further processing.

This pattern is all about providing a mechanism to allow a number of objects to attempt to handle a request, independently of any other object in the chain. Once the request is handled, it either exits from that point in the chain or travels along through the complete chain.
 

Why is it?


As discussed earlier in this article, generally handlers are selected through if/then/else or switch statements. The following problems are possible if we use this kind of statement

  • The client must know about the command handler objects, i.e. you have to define handlers explicitly in your code. So the client and the handler objects are tightly coupled.
  • As the process is decided at design time, it is almost impossible to determine if multiple "if" objects (determined at runtime) are candidates to handle a request without duplication of code.
  • System design in this way is less extensible and requires a lot of impact analysis while doing code changes in maintenance, as any changes in handler objects can easily break the client code.
Due to this complexity, the common solution to this is to apply the Chain of Responsibility pattern. The advantages are

  • It decouples the command from its handler objects. No direct explicit code is required to map between one and the other.
  • More than one handler can be made to process one command, and the client doesn't even need this information about such behaviour. So, such changes can be subtracted from the client.
  • As all handler objects are added to the chain, each can be associated or disassociated to the chain dynamically.


How is it?


In this pattern, one or more request handlers are associated to create a single chain. Each request handler object keeps a single reference to the next handler in the queue. The request is pushed to first handler in the queue that inspects whether it can process the request or not, and then it is pushed to the next handler in the queue and so on.

This is applicable to those scenarios where there are many different types of request and each request has specific processing requirements, where the processing logic is contained inside another object, and where we want both the request and the handler object to couple together explicitly in the code.

Let’s start with a scenario. I have an application (service) that talks to multiple devices running on multiple platforms (iPhone, Android and Windows). There are different applications for each mobile device type with which the application service communicates. For the sake of simplicity, I will use it like the base of our example.

The application service allows the user to perform following operations on any of the devices:

  • Fetch/Search Contact List
  • Fetch Messages
  • Fetch Memory Information
This is just three requirements, it could be increased in future. These are the requests that our application service will send to the device application. The device app understands these requests and returns the request with data as the response to the service; then the service collects this response and starts processing it.

The flow of our example is illustrated by the diagram below.
COR-Diagram.pngThe following Enum is defined to hold symbolic constant for each request that is going to be handled by the application.
 
        /// <summary>
        /// Enum to define different types of Requests.
        /// </summary>
        internal enum RequestType
        {
            Contact,
            Messages,
            Memory
        }  

Open in new window


First, an abstract class is defined that will be base class for all the concrete request classes that exist in our application. It holds only the abstract behaviour whose concrete implementation must be provided by the derived classes. A single parameter-less contractor is defined to keep track of the type of request, i.e. whether it is of type Contact or Message or Memory.

  
        /// <summary>
        ///  Base class for all Requests.
        /// </summary>
        internal abstract class RequestBase
        {
            public readonly RequestType type ;
            protected RequestBase(RequestType type)
            {
                this.type = type;
            }
        } 

Open in new window


Concrete request classes are defined which implement the RequestBase base class. The concrete request classes will hold data specific to their own requirements. Basically, these classes are DTOs (data transfer objects) that hold only data; they generally do not know anything about the data or how it will be processed. The processing algorithms and logic are contained in a separate class known as Request Handlers (in current context), which will be seen in action later in this article.

The following code demonstrates the three concrete implementations of the requests Contact RequestMessage Request and the Memory Request.

   #region Request Implementation

        /// <summary>
        /// Contact Request object to hold contact information.
        /// </summary>
        internal class ContactRequest : RequestBase
        {
            public int DeviceID { get; private set; }
            public string ContactName { get; private set; }

            public ContactRequest(int ID, string name) : base(RequestType.Contact)
            {
                this.DeviceID = ID;
                this.ContactName = name;
            }
        }

        /// <summary>
        /// Message Request object to hold Messages information.
        /// </summary>
        internal class MessagesRequest : RequestBase
        {
            
            public int DeviceID { get; private set; }
            public string Message { get; private set; }

            public MessagesRequest(int ID, string message)
                : base(RequestType.Messages)
            {
                this.DeviceID = ID;
                this.Message = message;
            }

        }

        /// <summary>
        /// Memory Request object to hold Memory information.
        /// </summary>
        internal class MemoryRequest : RequestBase
        {
            public int DeviceID { get; private set; }
            public int TotalMemory { get; private set; }
            public int MemoryLeft { get; private set; }

            public MemoryRequest(int ID, int totalMemory, int memoryLeft) : base(RequestType.Memory)
            {
                this.DeviceID = ID;
                this.TotalMemory = totalMemory;
                this.MemoryLeft = memoryLeft;
            }

        }

       
        #endRegion "End of Request Implementation"  

Open in new window


That's it. We have defined all the request classes that our application will deal with.

Now, we define another abstract class RequestHandlerBase that will be base class for all the request Handlers, as all handlers have some common behaviours to share. This base class will also serve the purpose of being the connecting link to create a queue or chain. These links will contain the reference to another object in the list; in our example NextHandler is going to hold the reference.

        /// <summary>
        /// Base class for all Request Handlers.
        /// </summary>
        internal abstract class RequestHandlerBase
        {
            private RequestHandlerBase NextHandler { get; set; }

            public void Execute(RequestBase request)
            {
                this.Process(request);
                PushNext(request);
            }

            protected abstract void Process(RequestBase request);

            public RequestHandlerBase SetNextHandler(RequestHandlerBase handler)
            {
                this.NextHandler = handler;
                return this.NextHandler;
            }
            
            private void PushNext(RequestBase request)
            {
                if (this.NextHandler != null)
                    this.NextHandler.Execute(request);
            }
        }  

Open in new window


Once the abstract class for the handler is defined, we are now ready to define concrete implementations for the Contact, Message and Memory requests. These request handlers know how to process specific requests and they hold the request's specific business logic. For example, the ContactRequestHandler object knows how to process the ContactRequest objects.

        #region Request Handler Implementation

        /// <summary>
        /// Contact Handler object to Handle Contact Request information.
        /// </summary>
        internal class ContactRequestHandler : RequestHandlerBase
        {
            protected override void Process(RequestBase request)
            {
                if (request.type == RequestType.Contact)
                {
                    var contact = (ContactRequest)request;
                    Console.WriteLine("Processing Contact Request. \n Device ID-> {0} - Contact Name ->{1}", contact.DeviceID, contact.ContactName);
                }
            }
        }

        /// <summary>
        /// Messages Handler object to Handle Message Request information.
        /// </summary>
        internal class MessagesRequestHandler : RequestHandlerBase
        {
            protected override void Process(RequestBase request)
            {
                if (request.type == RequestType.Messages)
                {
                    var message = (MessagesRequest)request;
                    Console.WriteLine("Processing Messages Request. \n Device ID-> {0} - Message ->{1}", message.DeviceID, message.Message);
                }
            }
        }

        /// <summary>
        /// Memory Handler object to Handle Memory Request information.
        /// </summary>
        internal class MemoryRequestHandler : RequestHandlerBase
        {
            protected override void Process(RequestBase request)
            {
                if (request.type == RequestType.Memory)
                {
                    var memory = (MemoryRequest)request;
                    Console.WriteLine("Processing Messages Request. \n Device ID-> {0} - Total Memory ->{1}  - Memory Left -> {2}", memory.DeviceID, memory.TotalMemory, memory.MemoryLeft);
                }
            }
        }
        
        #endregion "End of Request Handler Implementation"   

Open in new window


Now execute this. In the main section, we will create and associate all the handlers to form a chain, and then different requests are created that need to be processed.

Note : In a real application, the creation of the handlers may be delegated to the some builder objects that can have the mechanisms to dynamically create the lists of handlers based on certain rules. To keep this example simple and small, I have just created these objects in the main section itself.

 
        static void Main(string[] args)
        {
            // Create Request Handler Lists.
            var handler = new ContactRequestHandler();
            handler.SetNextHandler(new MessagesRequestHandler())
            .SetNextHandler(new MemoryRequestHandler());

            
            // Create Multiple requests.
            List request = new List();
            request.Add(new ContactRequest(2342244, "Nokia-X23"));
            request.Add(new MessagesRequest(2342244, "Hello Everyone ! how r u?"));
            request.Add(new MemoryRequest(2342244,2048,543));
            
            request.ForEach(x => {
                handler.Execute(x);
            });
        }

Open in new window


Once we execute, the following will be the output.

Pic-2.png
What is happening here is that we create lists of different requests, and each request is pushed to first handler object in the queue. That handler object checks the type of object it received to identify whether it should process this object or not. If the handler object found the request suitable for processing then it starts processing, and otherwise it simply ignores the request object and passes it to the next handler via the reference it holds. Thus the whole process works.

How does this affect the extensibility and maintainability of the application?

Suppose we are asked to implement a new requirement about Location to our sample application. The customer wants the application to capable of handling the Location information.

As this is a new request type, we add it to the RequestType Enum.

        /// <summary>
        /// Enum to define different types of Requests.
        /// </summary>
        internal enum RequestType
        {
            Contact,
            Messages,
            Memory,
            Location
        }  

Open in new window


Then define a LocationRequest class to represent the Location request to hold location specific data. Remember, it should be derived from the RequestBase class.

        /// <summary>
        /// Location Request object to hold Location information.
        /// </summary>
        internal class LocationRequest : RequestBase
        {
            public int DeviceID { get; private set; }
            public int Latitude  { get; private set; }
            public int Longitude  { get; private set; }

            public LocationRequest(int ID, int latitude, int longitude) : base(RequestType.Location)
            {
                this.DeviceID = ID;
                this.Latitude = latitude;
                this.Longitude = longitude;
            }

        }  

Open in new window


Then define a handler for this newly defined request. Let’s name it LocationRequestHandler class and make it derived from the RequestHandlerBase class.

 
        /// <summary>
        /// Location Handler object to Handle Location Request information.
        /// </summary>
        internal class LocationRequestHandler : RequestHandlerBase
        {
            protected override void Process(RequestBase request)
            {
                if (request.type == RequestType.Location)
                {
                    var location = (LocationRequest)request;
                    Console.WriteLine("Processing Location Request. \n Device ID-> {0} -  Latitude->{1}  - Longitude -> {2}", location.DeviceID, location.Latitude, location.Longitude);
                }
            }
        }

Open in new window


Attach this LocationReqeustHandler to the Handler lists, as we have done below in the main section.

 
        static void Main(string[] args)
        {
            // Create Request Handler Lists.
            var handler = new ContactRequestHandler();
            handler.SetNextHandler(new MessagesRequestHandler())
            .SetNextHandler(new MemoryRequestHandler())
            .SetNextHandler(new LocationRequestHandler());

            
            // Create Multiple requests.
            List<RequestBase> request = new List<RequestBase>();
            request.Add(new ContactRequest(2342244, "Nokia-X23"));
            request.Add(new MessagesRequest(2342244, "Hello Everyone ! how r u?"));
            request.Add(new MemoryRequest(2342244,2048,543));
            request.Add(new LocationRequest(2134324, 23434, 4645654));
            request.ForEach(x => {
                handler.Execute(x);
            });
        }   

Open in new window


After executing this, following result will be shown on the console.

Pic-1.png
We have successfully added a new feature to our example in the form of Request without changing the existing code much, so this allows the system to be more extensible and maintainable without the risk of breaking the existing code.

Points of Interest

This pattern is recommended when either of the following scenarios occur in your application:

  • When there are multiple objects that can handle a specific request. In other words, you don't want to specify handlers explicitly in your code.
  • When the handler is to be decided dynamically, i.e. which handler object should be allowed to handle the request objects is determined at runtime
  • If a request not being handled, then is this an acceptable scenario, i.e. no exception is thrown at any given point of time, but rather is simply ignored by the system.
  • When you want to decouple the request from its sender to its receiver or processor objects.

Remember:
 
  • Many other patterns like Mediator, Command and Observer also decouple the senders of the requests from its receiver’s objects, as the Chain of Responsibility pattern does, but they all achieve this in a different way. Chain of Responsibility uses the chain of handlers to pass the request through it.
  • Patterns can me mixed to achieve the best result. For example, the Command pattern can be used to wrap the request, and Chain of Responsibility can decouple the requests from their handlers.
ChainOfResponsibility.zip
0
Comment
0 Comments

Featured Post

Learn Ruby Fundamentals

This course will introduce you to Ruby, as well as teach you about classes, methods, variables, data structures, loops, enumerable methods, and finishing touches.

Join & Write a Comment

Key to your CPU's ability to stay cool is to use the right amount of thermal paste and apply it correctly. In other words you want as much thermal conductivity between CPU and the cooling block. Use a quality thermal paste and apply it in a manner…
See the Basics of Office 365's Note Taking app, OneNote

Keep in touch with Experts Exchange

Tech news and trends delivered to your inbox every month