Summary
Pattern Name | Chain of Responsibility Pattern |
Pattern Type | Behavioral Pattern |
Scope | Object |
Tagline | Chain the steps of processing a command |
Use cases | 1. 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 Patterns | Composite |
Difficulty Level | difficult |
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.
- 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.
- Concrete Handler Classes: classes that implement the handler interface and are responsible for handling the request.
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.