Design Pattern: Chain of Responsibility Pattern

Summary

Pattern NameChain of Responsibility Pattern
Pattern TypeBehavioral Pattern
ScopeObject
TaglineChain the steps of processing a command
Use cases1. When we want to chain the steps of processing one after another
and ensure choosing the steps on the fly during processing
2. When we want to decouple the sender and receiver of an operation
Related PatternsComposite
Difficulty Leveldifficult
Implementations

Definition

Chain of Responsibility pattern forms a chain of objects that is responsible for handling a command/operation request. As the responsibility is distributed into one or more objects and a single object is not responsible for handling a request, so that ensures the decoupling of the request sender and processor.

The request is sent to the chain of objects without specifying the handler. Which object will handle the request is not predefined, that is decided on-the-fly while processing.

Use Cases

Here are a few use cases of the Chain of Responsibility pattern-

  • When we want to decouple the receiver from the request sender.
  • When we need multiple handlers are required to handle a request, and those handlers need to be set at runtime.
  • When we want to distribute responsibility among objects, and allow them to add or remove responsibility dynamically at runtime.
  • When we don’t want to specify the receiver, any receiver can pick the request at runtime.

Implementation

Chain of Responsibility pattern has 2 elements involved in the implementation.

  1. Handler Interface: an interface(or abstract class) to ensure the common handler for the request. This defines a function for handling and might define a process for handling the next step.
  2. Concrete Handler Classes: classes that implement the handler interface and are responsible for handling the request.
Chain of Responsibility Structure Diagram

Examples

Example #1: Caching Data

Let’s take the example of caching data.

If the file is of type “CSS” or “JavaScript” we want to cache that in some CDN.

Or if the data is normal data then we want to cache it locally. We want to cache data less than 1024 characters in Redis and larger data in the disk.

General Class for Data [not related to the pattern implementation]

enum DATA_TYPE {
    DATA,
    JAVASCRIPT,
    CSS
}

class Data

    var type: DATA_TYPE
    var data: String
    var key: String

    constructor(typeParam: DATA_TYPE, keyParam: String, dataParam: String) 
       type = typeParam
       key = keyParam
       data = dataParam
    end constructor

    method getType(): DATA_TYPE
        return type
    end method

    method getKey(): String
        return key
    end method

    method getData(): String
        return data
    end method

end class

Cache Handler and Concrete Handler Classes

// CacheHandler abstract class

abstract class CacheHandler

   var nextCacheHandler: CacheHandler

    constructor(nextCacheHandlerParam: CacheHandler)
       nextCacheHandler = nextCacheHandlerParam
    end constructor

    abstract handleRequest(Data data)

end class


// CdnCacheHandler

class CdnCacheHandler extends CacheHandler

    constructor(nextCacheHandlerParam: CacheHandler) 
        super(nextCacheHandlerParam)
    end constructor

    method handleRequest(Data data)
        if (data.getType() == DATA_TYPE.CSS || data.getType() == DATA_TYPE.JAVASCRIPT)
            // Write code to send the data file to some CDN

            output "Caching file '" + data.getKey() + "' in CDN"
        else if (nextCacheHandler != null)
            nextCacheHandler.handleRequest(data)
        end if
    end method

end class


// RedisCacheHandler

class RedisCacheHandler extends CacheHandler

    constructor(nextCacheHandlerParam: CacheHandler) 
        super(nextCacheHandlerParam)
    end constructor

    method handleRequest(Data data)
        if (data.getType() == DATA_TYPE.DATA && data.getData().length() <= 1024)
            // Write code to cache data in Redis

            output "Caching data '" + data.getKey() + "' in Redis"
        else if (nextCacheHandler != null)
            nextCacheHandler.handleRequest(data)
        end if
    end method

end class


// DiskCacheHandler

class DiskCacheHandler extends CacheHandler

    constructor(nextCacheHandlerParam: CacheHandler) 
        super(nextCacheHandlerParam)
    end constructor

    
    method handleRequest(Data data) {
        if (data.getType() == DATA_TYPE.DATA && data.getData().length() > 1024)
            // Write code to cache data in Disk

            output "Caching data '" + data.getKey() + "' in Disk"
        else if (nextCacheHandler != null)
            nextCacheHandler.handleRequest(data)
        end if
    end method

end class

Demo

var cacheHandler: DiskCacheHandler = new DiskCacheHandler(new RedisCacheHandler(new CdnCacheHandler(null)))

// First request 
var data: Data = new Data(DATA_TYPE.DATA, "key1", "ABC320489un3429rn29urn29r82n9jfdn2")

cacheHandler.handleRequest(data)

// Second request
data = new Data(DATA_TYPE.CSS, "key2", ".some-class{border: 1px solid red; margin: 10px}")

cacheHandler.handleRequest(data)

Output

Caching data 'key1' in Redis

Caching file 'key2' in CDN

Code Implementations

Use the following links to check the Chain of Responsibility pattern implementation in specific programming languages.

Leave a Comment


The reCAPTCHA verification period has expired. Please reload the page.