iOS设计模式之责任链

558 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第15天,点击查看活动详情

  • 本文主要介绍iOS设计模式中的责任链模式,责任链顾名思义就是不同对象负责的责任不同,把事件连着一条链条进行处理。

1. 什么是责任链模式

谁也不能无所不知,俗话说“人多智广”。但是此话不假,那么把很多人的智慧连成一个链条会更好。每个人都有自己的专长,联合起来就能形成强大的实体。这很像邻里间的互相帮助,或者项目团队成员之间的合作。智慧链条中的每个单元都可以为问题的解决作出贡献。如果一个人不知道如何解决问题,他就会把问题沿着链条向下传,也许就有人能够解决问题。有的时候,即使有人知道如何解决,依然会把问题传下去。这样就能完成解决问题的特定过程
假如你正在开发一个在线订购系统。 你希望对系统访问进行限制, 只允许认证用户创建订单。 此外, 拥有管理权限的用户也拥有所有订单的完全访问权限
简单规划后, 你会意识到这些检查必须依次进行。 只要接收到包含用户凭据的请求, 应用程序就可尝试对进入系统的用户进行认证。 但如果由于用户凭据不正确而导致认证失败, 那就没有必要进行后续检查了。
后续又添加了一些判断条件或者流程, 检查代码本来就已经混乱不堪, 而每次新增功能都会使其更加臃肿。 修改某个检查步骤有时会影响其他的检查步骤。 最糟糕的是, 当你希望复用这些检查步骤来保护其他系统组件时, 你只能复制部分代码, 因为这些组件只需部分而非全部的检查步骤。这个时候我们使用责任链模式就可以让不同对象负责不同的判读或行为,我们根据我们的需求组合成任意顺序的链,这就是责任链
这种概念对于面向对象的软件设计同样适用。比如让一组对象处理特定的请求,而对这个组添加或删除处理程序都不应影响组的完整性。
责任链模式的主要思想是,对象引用了同一类型的另一个对象,形成一条链。链中的每个对象实现了同样的方法,处理对联中第一个对象发起的同一个请求。如果一个对象不知道如何处理请求,他就把请求传给下一个相应器

责任链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间发生耦合。此模式将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。

2. 什么时候使用责任链模式

以下情况考虑使用

  • 有多个对象可以处理请求,而处理程序只有运行时才能确定,
  • 一组对象发出请求,而不想显示制定处理请求的特定处理程序

3.代码展示

import XCTest

/// The Handler interface declares a method for building the chain of handlers.
/// It also declares a method for executing a request.
protocol Handler: class {

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

    func handle(request: String) -> String?

    var nextHandler: Handler? { get set }
}

extension Handler {

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

        /// Returning a handler from here will let us link handlers in a
        /// convenient way like this:
        /// monkey.setNext(handler: squirrel).setNext(handler: dog)
        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 handler
/// 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.
class ChainOfResponsibilityConceptual: XCTestCase {
 
    func test() {

        /// The other part of the client code constructs the actual chain.

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

        /// The client should be able to send a request to any handler, not just
        /// the first one in the chain.

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

执行结果

Chain: Monkey > Squirrel > Dog


Client: Who wants a Nut?

Squirrel: I'll eat the Nut.

Client: Who wants a Banana?

Monkey: I'll eat the Banana.

Client: Who wants a Cup of coffee?

Cup of coffee was left untouched.


Subchain: Squirrel > Dog


Client: Who wants a Nut?

Squirrel: I'll eat the Nut.

Client: Who wants a Banana?

Banana was left untouched.

4. 总结

我们也可以把RPG游戏中人物的各种防御机制的实现为责任链模式。每种防御机制只能应对一种特定的攻击。一个攻击处理程序链决定了人物可以防御何种攻击。在游戏过程中,任何攻击处理程序都能在任何时间被添加或删除,而不会影响人物的其他行为,对于此类设计,责任链模式是很自然的选择。则否,攻击处理程序的复杂组合会让人物的代码非常庞大,让处理的程序的变更变得非常困难。