Chain of Responsibility Design Pattern in Kotlin

1. Definition

The Chain of Responsibility Design Pattern decouples the sender from the receiver by allowing multiple objects to handle the request. Each handler decides whether to process the request or pass it along the chain to the next handler.

2. Problem Statement

Imagine a situation where a request needs to be processed by one of several handlers, but you don't know beforehand which one will be able to handle it. Directly coupling the request to a specific handler may be inefficient or impractical.

3. Solution

Instead of tying the request to a specific handler, create a chain of potential handlers. The request travels along the chain until a handler takes care of it or it reaches the end of the chain.

4. Real-World Use Cases

1. Event handling systems in GUI libraries where an event can be handled by a component or its parent.

2. Middleware in web servers that process requests in sequence.

3. Workflow systems where tasks are processed in stages.

5. Implementation Steps

1. Define the handler interface or abstract class with a method to set the next handler and another to handle the request.

2. Create concrete handler classes that implement the handler interface or extend the abstract class.

3. Build the chain by connecting handlers, and send the request through the chain.

6. Implementation in Kotlin Programming

// Step 1: Handler Interface
interface Handler {
    var nextHandler: Handler?
    fun handleRequest(request: String): Boolean
}
// Step 2: 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
    }
}
// Step 3: Building the chain and testing
fun main() {
    val firstHandler = FirstHandler()
    val secondHandler = SecondHandler()
    firstHandler.nextHandler = secondHandler
    listOf("Request1", "Request2", "Request3").forEach {
        if (!firstHandler.handleRequest(it)) {
            println("$it was not handled")
        }
    }
}

Output:

FirstHandler handled Request1
SecondHandler handled Request2
Request3 was not handled

Explanation:

1. The Handler interface declares methods for handling requests and setting the next handler in the chain.

2. FirstHandler and SecondHandler are concrete handlers that check if they can handle a request. If not, they pass it to the next handler.

3. In the main function, the handlers are linked to form a chain and then various requests are sent through the chain.

7. When to use?

The Chain of Responsibility pattern is beneficial when:

1. More than one object might handle a request, and the handler isn't explicitly known in advance.

2. You want to issue a request to multiple objects without coupling the sender to the receivers.

3. The set of handlers can be dynamically rearranged or changed.

Comments