Chain of Responsibility – Design Patterns in Swift

Introduction

Implementing a request directly within the class that sends the request is inflexible because it couples the class to a particular receiver and makes it impossible to support multiple receivers. Therefore, Chain of Responsibility pattern borns. This pattern solves coupling the sender of a request to its receiver should be avoided and  more than one receiver can handle a request.

Chain of Responsibility – UML

Use Chain of Responsibility In Swift

The Handler interface declares a method for building the chain of handlers. It also declares a method for executing a request.

import XCTest

protocol Handler: class {

    @discardableResult
    func setNext(handler: Handler) -> Handler

    func handle(request: String) -> String?

    var nextHandler: Handler? { get set }
}

Returning a handler from here will let us link handlers in a convenient way like this: monkey.setNext(handler: squirrel).setNext(handler: dog).

extension Handler {

    func setNext(handler: Handler) -> Handler {
        self.nextHandler = handler

        return handler
    }

    func handle(request: String) -> String? {
        return nextHandler?.handle(request: request)
    }
}

All Concrete Handlers either handle a request or pass it to the next handle in the chain.

class MonkeyHandler: Handler {

    var nextHandler: Handler?

    func handle(request: String) -> String? {
        if (request == "Banana") {
            return "Monkey: I'll eat the " + request + ".\n"
        } else {
            return nextHandler?.handle(request: request)
        }
    }
}

class SquirrelHandler: Handler {

    var nextHandler: Handler?

    func handle(request: String) -> String? {

        if (request == "Nut") {
            return "Squirrel: I'll eat the " + request + ".\n"
        } else {
            return nextHandler?.handle(request: request)
        }
    }
}

class DogHandler: Handler {

    var nextHandler: Handler?

    func handle(request: String) -> String? {
        if (request == "MeatBall") {
            return "Dog: I'll eat the " + request + ".\n"
        } else {
            return nextHandler?.handle(request: request)
        }
    }
}

The client code is usually suited to work with a single handler. In most cases, it is not even aware that the handler is part of a chain.

class Client {

    static func someClientCode(handler: Handler) {

        let food = ["Nut", "Banana", "Cup of coffee"]

        for item in food {

            print("Client: Who wants a " + item + "?\n")

            guard let result = handler.handle(request: item) else {
                print("  " + item + " was left untouched.\n")
                return
            }

            print("  " + result)
        }
    }
}

Let’s see how it all works together. The other part of the client code constructs the actual chain. The client should be able to send a request to any handler, not just the first one in the chain.

class ChainOfResponsibilityConceptual: XCTestCase {
 
    func test() {

        let monkey = MonkeyHandler()
        let squirrel = SquirrelHandler()
        let dog = DogHandler()
        monkey.setNext(handler: squirrel).setNext(handler: dog)

        print("Chain: Monkey > Squirrel > Dog\n\n")
        Client.someClientCode(handler: monkey)
        print()
        print("Subchain: Squirrel > Dog\n\n")
        Client.someClientCode(handler: squirrel)
    }
}

Conclusion

In summary, the pattern is recognizable by behavioral methods of one group of objects that indirectly call the same methods in other objects, while all the objects follow the common interface. The Chain of Responsibility pattern isn’t a frequent guest in a Swift program since it’s only relevant when code operates with chains of objects.

Leave a Reply

Your email address will not be published. Required fields are marked *