iOS设计模式之访问者

2,366 阅读4分钟

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

  • 本文主要介绍iOS设计模式中的访问者,访问者模式是一种行为拓展的方式,

1. 什么是访问者模式

比如说,你家里的管道坏了,自己不会修,虽然你是房子的主人,但是并不意味着你对它里面的一切都了如指掌。因此解决这个问题最好的方式就是尽快找个行家来修理

假如有这样一位非常希望赢得新客户的资深保险代理人。 他可以拜访街区中的每栋楼, 尝试向每个路人推销保险。 所以, 根据大楼内组织类型的不同, 他可以提供专门的保单:如果建筑是居民楼, 他会推销医疗保险。如果建筑是银行, 他会推销失窃保险。如果建筑是咖啡厅, 他会推销火灾和洪水保险。

访问者模式涉及两个关键角色(或者说组件):访问者它访问的元素。元素可以是任何对象,但通常是组合模式中结构的节点。元素本身不限于这些种类的结构。访问者知道复杂结构中每个元素,可以访问每个元素的节点,并根据元素的特征,属性或操作执行任何操作

访问者模式: 表示一个作用越某个对象结构中的各元素的操作。它让我们可以在不改变与阿苏的类前提下定义作用于这些元素的新操作

2. 什么时候使用访问者

在以下的场景,可以使用访问者模式。

  • 一个复杂的对象结构包含很多其他对象,它们有不同的接口(比如组合体),但是想对这些对象是是一些依赖于其具体类型的操作。
  • 需要对一个组合结构中的对象进行很多不相关的操作,但是不想让这些操作“污染”这些对象的类。可以将相关的操作集中起来,定义在一个访问者类中,并在需要在访问者中定义的操作时使用它。
  • 定义复杂结构的类很少作修改,但经常需要向其添加新的操作

3.代码展示

import XCTest

/// The Component interface declares an `accept` method that should take the
/// base visitor interface as an argument.
protocol Component {

    func accept(_ visitor: Visitor)
}

/// Each Concrete Component must implement the `accept` method in such a way
/// that it calls the visitor's method corresponding to the component's class.
class ConcreteComponentA: Component {

    /// Note that we're calling `visitConcreteComponentA`, which matches the
    /// current class name. This way we let the visitor know the class of the
    /// component it works with.
    func accept(_ visitor: Visitor) {
        visitor.visitConcreteComponentA(element: self)
    }

    /// Concrete Components may have special methods that don't exist in their
    /// base class or interface. The Visitor is still able to use these methods
    /// since it's aware of the component's concrete class.
    func exclusiveMethodOfConcreteComponentA() -> String {
        return "A"
    }
}

class ConcreteComponentB: Component {

    /// Same here: visitConcreteComponentB => ConcreteComponentB
    func accept(_ visitor: Visitor) {
        visitor.visitConcreteComponentB(element: self)
    }

    func specialMethodOfConcreteComponentB() -> String {
        return "B"
    }
}

/// The Visitor Interface declares a set of visiting methods that correspond to
/// component classes. The signature of a visiting method allows the visitor to
/// identify the exact class of the component that it's dealing with.
protocol Visitor {

    func visitConcreteComponentA(element: ConcreteComponentA)
    func visitConcreteComponentB(element: ConcreteComponentB)
}

/// Concrete Visitors implement several versions of the same algorithm, which
/// can work with all concrete component classes.
///
/// You can experience the biggest benefit of the Visitor pattern when using it
/// with a complex object structure, such as a Composite tree. In this case, it
/// might be helpful to store some intermediate state of the algorithm while
/// executing visitor's methods over various objects of the structure.
class ConcreteVisitor1: Visitor {

    func visitConcreteComponentA(element: ConcreteComponentA) {
        print(element.exclusiveMethodOfConcreteComponentA() + " + ConcreteVisitor1\n")
    }

    func visitConcreteComponentB(element: ConcreteComponentB) {
        print(element.specialMethodOfConcreteComponentB() + " + ConcreteVisitor1\n")
    }
}

class ConcreteVisitor2: Visitor {

    func visitConcreteComponentA(element: ConcreteComponentA) {
        print(element.exclusiveMethodOfConcreteComponentA() + " + ConcreteVisitor2\n")
    }

    func visitConcreteComponentB(element: ConcreteComponentB) {
        print(element.specialMethodOfConcreteComponentB() + " + ConcreteVisitor2\n")
    }
}

/// The client code can run visitor operations over any set of elements without
/// figuring out their concrete classes. The accept operation directs a call to
/// the appropriate operation in the visitor object.
class Client {
    // ...
    static func clientCode(components: [Component], visitor: Visitor) {
        // ...
        components.forEach({ $0.accept(visitor) })
        // ...
    }
    // ...
}

/// Let's see how it all works together.
class VisitorConceptual: XCTestCase {

    func test() {
        let components: [Component] = [ConcreteComponentA(), ConcreteComponentB()]

        print("The client code works with all visitors via the base Visitor interface:\n")
        let visitor1 = ConcreteVisitor1()
        Client.clientCode(components: components, visitor: visitor1)

        print("\nIt allows the same client code to work with different types of visitors:\n")
        let visitor2 = ConcreteVisitor2()
        Client.clientCode(components: components, visitor: visitor2)
    }
}

执行结果

The client code works with all visitors via the base Visitor interface:

A + ConcreteVisitor1

B + ConcreteVisitor1


It allows the same client code to work with different types of visitors:

A + ConcreteVisitor2

B + ConcreteVisitor2

4. 总结

访问者是一种行为设计模式, 允许你在不修改已有代码的情况下向已有类层次结构中增加新的行为。访问者模式是拓展组合结构功能的一种强有力的方式。如何组合结构具有精心设计的基本操作,而且结构将来也不会变更,就可以使用访问者模式。访问者不是常用的设计模式, 因为它不仅复杂, 应用范围也比较狭窄