迭代器模式 Iterator Pattern

迭代器模式(Iterator Pattern)屬于行為型模式。Iterator pattern 提供了循環(huán)集合的標(biāo)準(zhǔn)方法。

Iterator pattern 包含以下兩部分:

IteratorPatternUML.png
  1. IteratorProtocol:Swift 中的IterableProtocol協(xié)議定義了一個可以使用 for in 循環(huán)迭代的類型。
  2. Iterator Object:想要進(jìn)行迭代的對象。一般,Iterator object 不直接遵守IteratorProtocol協(xié)議,而是遵守Sequence協(xié)議。Sequence協(xié)議遵守IteratorProtocol協(xié)議。通過遵守Sequence協(xié)議可以直接獲得許多高級函數(shù),例如,mapfilter等。

如果你對該協(xié)議不了解,可以查看IterableProtocol、Sequence文檔。

在這篇文章中,將創(chuàng)建自定義 struct。該對象遵守Sequence協(xié)議,并使用Sequence協(xié)議中方法進(jìn)行排序。

何時使用 Iterator Pattern

當(dāng)擁有一組有序?qū)ο蟮念惢蚪Y(jié)構(gòu),并且希望使用 for in 循環(huán)使其可迭代時,請使用迭代器模式。

示例

這篇文章將會創(chuàng)建 Queue 對象。Queue 是一個列表,只能從尾部插入對象,從頭部移除對象。這樣可以確保第一個入隊的對象也是第一個出隊的對象,先進(jìn)先出(first come, first serve)。

創(chuàng)建Queue.swift文件,并添加以下代碼:

import Foundation

public struct Queue<T> {
    // 數(shù)組元素可以為任意類型。
    private var array: [T?] = []
    
    // Queue 的頭部是數(shù)組第一個元素
    private var head = 0
    
    // 確認(rèn)數(shù)組是否為空
    public var isEmpty: Bool {
        return count == 0
    }
    
    // Queue 內(nèi)對象數(shù)量
    public var count: Int {
        return array.count - head
    }
    
    // 添加對象到 Queue
    public mutating func enqueue(_ element: T) {
        array.append(element)
    }
    
    // 從 Queue 隊列移除對象
    public mutating func dequeue() -> T? {
        guard head < array.count,
            let element = array[head] else {
                return nil
        }
        
        array[head] = nil
        head += 1
        
        let percentage = Double(head)/Double(array.count)
        
        if array.count > 50,
            percentage > 0.25 {
            array.removeFirst(head)
            head = 0
        }
        
        return element
    }
}

在上述代碼中,enqueue()方法用于添加對象到 Queue;dequeue()方法用于從 Queue 隊列移除對象。

創(chuàng)建Ticket.swift文件,并添加以下代碼:

import Foundation

public struct Ticket {
    var description: String
    var priority: PriorityType
    
    enum PriorityType {
        case low
        case medium
        case high
    }
    
    init(description: String, priority: PriorityType) {
        self.description = description
        self.priority = priority
    }
}

進(jìn)入ViewController.swift文件,在viewDidLoad()方法內(nèi)添加以下代碼:

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        // 初始化 Queue 對象,并添加四個對象。
        var queue = Queue<Ticket>()
        queue.enqueue(Ticket(description: "Wireframe Tinder for dogs app", priority: .low))
        queue.enqueue(Ticket(description: "Set up 4k monitor for Josh", priority: .medium))
        queue.enqueue(Ticket(description: "There is smoke coming out of my laptop", priority: .high))
        queue.enqueue(Ticket(description: "Put googly eyes on the Roomba", priority: .low))
        
        // 可以看到 dequeue 的是第一個添加的對象
        let element = queue.dequeue()
        print((element?.description ?? "No Description") + "\n")
    }

在實(shí)際應(yīng)用中,肯定希望能夠按照優(yōu)先級對 Ticket 進(jìn)行排序。根據(jù)現(xiàn)在的情況,只能編寫多個 if 語句進(jìn)行排序。使用 Swift 內(nèi)置的排序函數(shù)能夠節(jié)省大量時間。

如果現(xiàn)在嘗試使用 for in 、sorted()進(jìn)行排序,編譯器會報錯。因為使用這些方法前需要先遵守Sequence協(xié)議。為Queue添加以下 extension:

extension Queue: Sequence {
    public func makeIterator() -> IndexingIterator<ArraySlice<T?>> {
        // 只枚舉非空對象
        let nonEmptyValues = array[head ..< array.count]
        return nonEmptyValues.makeIterator()
    }
}

遵守Sequence協(xié)議時,有兩點(diǎn)需要注意:

  • 第一點(diǎn)為關(guān)聯(lián)類型(associated type),也就是迭代器(Iterator)。在上述代碼中,associated type 為IndexingIterator。如果集合沒有聲明自定義迭代器,則默認(rèn)使用IndexingIterator。
  • 第二點(diǎn)為Iterator協(xié)議,其為makeIterator函數(shù)不可缺少的。Iterator協(xié)議為 class 、struct 構(gòu)造迭代器。

ViewController.swift文件中viewDidLoad()方法內(nèi)添加以下代碼:

    override func viewDidLoad() {
        ...
        
        // 枚舉 queue 并輸出
        print("List of Tickets in queue:")
        for ticket in queue {
            print(ticket?.description ?? "No Description")
        }
    }

輸出如下:

List of Tickets in queue:
Set up 4k monitor for Josh
There is smoke coming out of my laptop
Put googly eyes on the Roomba

在使用排序函數(shù)前,為Ticket添加以下 extension:

extension Ticket {
    var sortIndex: Int {
        switch self.priority {
        case .low:
            return 0
        case .medium:
            return 1
        case .high:
            return 2
        }
    }
}

為優(yōu)先級設(shè)置數(shù)字能夠簡化排序,排序時將使用sortIndex做為依據(jù)。在ViewController.swift文件中viewDidLoad()方法內(nèi)添加以下代碼:

    override func viewDidLoad() {
        ...
        
        // 進(jìn)行排序
        let sortedTickets = queue.sorted {
            $0!.sortIndex > ($1?.sortIndex)!
        }
        
        // 循環(huán) sortedTickets 數(shù)組,將元素添加到 sortedQueue
        var sortedQueue = Queue<Ticket>()
        for ticket in sortedTickets {
            sortedQueue.enqueue(ticket!)
        }
        
        // 輸出 sotedQueue 內(nèi)對象,可以看到 first in, firt out。
        print("\n")
        print("Tickets sorted by priority:")
        for ticket in sortedQueue {
            print(ticket?.description ?? "No Description")
        }

如此輕松地對集合進(jìn)行排序是一項強(qiáng)大的功能。隨著集合變大,排序功能將變得更有價值。

總結(jié)

通過遵守IteratorProtocol協(xié)議,可以自定義迭代對象的方式。例如,可以只實(shí)現(xiàn)一個next()方法,在該方法內(nèi)返回迭代中的下一個對象。通常,極少需要直接遵守IteratorProtocol協(xié)議。即使需要自定義迭代器,遵守Sequence協(xié)議也是一個更好的選擇。

以下是 Iterator Pattern 的關(guān)鍵點(diǎn):

  • Iterator pattern 提供了使用 for in 語法循環(huán)遍歷集合的標(biāo)準(zhǔn)方法。
  • 自定義對象最好遵守Sequence協(xié)議,而非直接遵守IteratorProtocol協(xié)議。
  • 通過遵守Sequence協(xié)議,能夠直接獲得更為高級的功能(如map、filter等)。

Demo名稱:IteratorPattern
源碼地址:https://github.com/pro648/BasicDemos-iOS/tree/master/IteratorPattern

參考資料:

  1. Design Patterns in Swift: Iterator Pattern
  2. Iterator pattern
  3. IteratorProtocol
  4. Sequence

歡迎更多指正:https://github.com/pro648/tips/wiki

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容