1. Definition
The Chain of Responsibility Design Pattern decouples the sender from the receiver by allowing more than one object to handle a request. These handlers are organized as a chain, and the request is passed along the chain until an object handles it or it reaches the end of the chain.
2. Problem Statement
Imagine a series of checks or processes that a request needs to pass through. Instead of hardcoding a sequence of calls or creating a tight coupling between objects, you'd prefer a more dynamic and decoupled way to process the request.
3. Solution
Establish a chain of objects, where each object can either handle the request or pass it to the next object in the chain. This way, the sender sends the request to the first object in the chain, and it travels along the chain until it's handled or reaches the end.
4. Real-World Use Cases
1. Event systems where events can be handled by multiple listeners.
2. Middleware in web servers, where requests pass through a series of processing stages.
3. Input validation and processing in software applications.
5. Implementation Steps
1. Define the handler interface or abstract class, which specifies a method to handle requests and set the next handler.
2. Implement concrete handlers, which decide whether to process the request or pass it along.
3. Link these handlers to form a chain.
6. Implementation in Kotlin
// Step 1: Define the handler interface
interface Handler {
var nextHandler: Handler?
fun handleRequest(request: String): Boolean
}
// Step 2: Implement concrete handlers
class FirstHandler : Handler {
override var nextHandler: Handler? = null
override fun handleRequest(request: String): Boolean {
if (request == "Request1") {
println("FirstHandler handled $request")
return true
}
return nextHandler?.handleRequest(request) ?: false
}
}
class SecondHandler : Handler {
override var nextHandler: Handler? = null
override fun handleRequest(request: String): Boolean {
if (request == "Request2") {
println("SecondHandler handled $request")
return true
}
return nextHandler?.handleRequest(request) ?: false
}
}
fun main() {
val firstHandler = FirstHandler()
val secondHandler = SecondHandler()
firstHandler.nextHandler = secondHandler
listOf("Request1", "Request2", "Request3").forEach { request ->
val isHandled = firstHandler.handleRequest(request)
if (!isHandled) {
println("$request was not handled")
}
}
}
Output:
FirstHandler handled Request1 SecondHandler handled Request2 Request3 was not handled
Explanation:
1. We first defined a Handler interface that mandates implementing the handleRequest method and having a nextHandler property.
2. We created two concrete handlers: FirstHandler and SecondHandler. Each handler checks if it can handle the request; if not, it passes the request to its next handler.
3. In the main function, we set up the chain by assigning secondHandler as the next handler for firstHandler. We then test the chain by sending three requests. The first two are handled by the respective handlers, while the third isn't handled by any handler in the chain.
7. When to use?
The Chain of Responsibility pattern is useful when:
1. Multiple objects can handle a request, and the handler isn't known a priori, it's determined dynamically.
2. The set of objects that can handle a request should be specified dynamically.
3. Requests should be handled by multiple objects in a chain or sequence.
Comments
Post a Comment