[Swift设计模式] 迭代器

1,005 阅读4分钟
更多内容,欢迎关注公众号:Swift花园
喜欢文章?不如来点赞关注吧

这一篇文章将聚焦在迭代器设计模式。Swift标准库中大量地使用了这种模式,它们是一些你需要创建迭代器时会用到的协议。不过老实说:我自己从来没直接实现过这种模式。😅

事实上,99%的情况你都不需要面对这种模式,因为Swift内建的迭代器已经惊人的好用了。尽量用序列,数组和字典,而不是直接实现这个模式。不过,有机会了解事物的底层工作机制,总归是好的,不是吗? 🙃

 

什么是迭代器设计模式?

如字面上说的,这种模式让你能够迭代一个合集里的元素。设计模式四人帮的书上有一个定义:

提供一种方法,访问一个容器对象中的各个元素,而又不暴露该对象的内部细节。

简单来说,迭代器为你提供一个接口,让你可以遍历合集,不用关心这个合集是怎么实现的。下面是一个用字符串迭代器演示上面定义的粒子。

import Foundation

protocol StringIterator {
    func next() -> String?
}

class ArrayStringIterator: StringIterator {

    private let values: [String]
    private var index: Int?

    init(_ values: [String]) {
        self.values = values
    }

    private func nextIndex(for index: Int?) -> Int? {
        if let index = index, index < self.values.count - 1 {
            return index + 1
        }
        if index == nil, !self.values.isEmpty {
            return 0
        }
        return nil
    }
    
    func next() -> String? {
        if let index = self.nextIndex(for: self.index) {
            self.index = index
            return self.values[index]
        }
        return nil
    }
}


protocol Iterable {
    func makeIterator() -> StringIterator
}

class DataArray: Iterable {

    private var dataSource: [String]

    init() {
        self.dataSource = ["🐶", "🐔", "🐵", "🦁", "🐯", "🐭", "🐱", "🐮", "🐷"]
    }

    func makeIterator() -> StringIterator {
        return ArrayStringIterator(self.dataSource)
    }
}

let data = DataArray()
let iterator = data.makeIterator()

while let next = iterator.next() {
    print(next)
}

如你所见,有两个主要的协议,各自都有一个简单的实现。 DataArray类现在就像一个真正的数组,内部的元素可以用循环来遍历。让我们再深挖一下理论,用 Swift 标准库里的组件来重新实现上面的例子。😉

 

Swift中的自定义序列

Swift有一个内建的序列协议,可以帮助你创建迭代器。实现你自己的序列其实就是做一件事:通过创建自定义的迭代器隐藏内部的数据结构体。你只需要存储当前的索引,然后在每一次 next 函数被调用时返回下一个元素。😛

import Foundation

struct Emojis: Sequence {
    let animals: [String]

    func makeIterator() -> EmojiIterator {
        return EmojiIterator(self.animals)
    }
}

struct EmojiIterator: IteratorProtocol {

    private let values: [String]
    private var index: Int?
    
    init(_ values: [String]) {
        self.values = values
    }
    
    private func nextIndex(for index: Int?) -> Int? {
        if let index = index, index < self.values.count - 1 {
            return index + 1
        }
        if index == nil, !self.values.isEmpty {
            return 0
        }
        return nil
    }
    
    mutating func next() -> String? {
        if let index = self.nextIndex(for: self.index) {
            self.index = index
            return self.values[index]
        }
        return nil
    }
}

let emojis = Emojis(animals: ["🐶", "🐔", "🐵", "🦁", "🐯", "🐭", "🐱", "🐮", "🐷"])
for emoji in emojis {
    print(emoji)
}

因此,Sequence 协议相当于前一个例子中的那个 Iterable 协议,而 IteratorProtocol 则类似于对应的 StringIterator,不过更像 Swift 风格,并且更通用。

你现在已经了解如何创建自定义序列了,这在隐藏数据结构体和提供泛型可迭代接口时会很有用。 🤔

我还想介绍一个非常强大的东西,它是在 Swift 标准库里头。准备好了吗?

 

Swift 中的自定义 Collection

Collection 是比序列更进一步的东西,里面的元素可以通过下标访问。Collection 还定义了 startIndex 和 endIndex,并且每个独立元素都可以多次访问。听起来不错吧?👍

某些情况下,创建自定义 Collection 类型非常有用。举个例子,如果你想消除可选型。想象一个分类收藏的机制,对于每个分类都有一组收藏。你需要处理空的情况和不存在的情况。利用自定义 Collection,你可以在你的自定义数据结构里隐藏额外的代码,只暴露一个干净的接口。😍

class Favorites {

    typealias FavoriteType = [String: [String]]

    private(set) var list: FavoriteType

    public static let shared = Favorites()

    private init() {
        self.list = FavoriteType()
    }
}


extension Favorites: Collection {

    typealias Index = FavoriteType.Index
    typealias Element = FavoriteType.Element
    
    var startIndex: Index {
        return self.list.startIndex
    }
    var endIndex: Index {
        return self.list.endIndex
    }
    
    subscript(index: Index) -> Iterator.Element {
        return self.list[index]
    }

    func index(after i: Index) -> Index {
        return self.list.index(after: i)
    }
}

extension Favorites {
    
    subscript(index: String) -> [String] {
        return self.list[index] ?? []
    }

    func add(_ value: String, category: String) {
        if var values = self.list[category] {
            guard !values.contains(value) else {
                return
            }
            values.append(value)
            self.list[category] = values
        }
        else {
            self.list[category] = [value]
        }
    }
    
    func remove(_ value: String, category: String) {
        guard var values = self.list[category] else {
            return
        }
        values = values.filter { $0 == value }
        
        if values.isEmpty {
            self.list.removeValue(forKey: category)
        }
        else {
            self.list[category] = values
        }
    }
}


Favorites.shared.add("apple", category: "fruits")
Favorites.shared.add("pear", category: "fruits")
Favorites.shared.add("apple", category: "fruits")

Favorites.shared["fruits"]

Favorites.shared.remove("apple", category: "fruits")
Favorites.shared.remove("pear", category: "fruits")
Favorites.shared.list

我知道这个例子看起来有点呆,不过很恰当地演示了为什么合集比序列更强大。🤐

 

我的公众号
这里有Swift及计算机编程的相关文章,以及优秀国外文章翻译,欢迎关注~