Try   HackMD

Iterator 迭代器模式

Overview of Content

Iterator 使用場景

  1. 切割出遍歷物件,對於 不同物件可有不同遍歷方法

  2. 一個物件的遍歷如果透過內部自己提供,會使的該物件承擔過多責任 (也不易維護),但是如果提供給使用者就暴露了內部細節,這時就可以使用 跌代模式

  3. 提供一個方法取得容器物件的每個元素,又不對外暴露

Iterator 定義 & Iterator UML

  • Iterator 的定義

    提供一種方法(界面),用來訪問一個容器中的所有元素,而不對外暴露細節

  • Iterator 角色介紹

    角色 說明
    Aggregate (容器接口) 提供一個類創建 Iterator 的方法
    ConcreteAggreate 實現容器的內容,內部有一個存放數據的容器
    Iterator (迭代接口) 負責訪問、遍歷容器的核心方法
    ConcreteIterator 實際去遍歷容器中的數據,內部會有一個 cursor 去紀錄使用者的操作行為

    • 圖中 Aggregate 的方法對應 Java#Iterator 的方法如下表

      Aggregate#Method Java Iterator#Method
      First() -
      IsDone() hasNext()
      Next() next()
      CurrentItem() -
      - remove()

Iterator 設計 - 優缺點

  • Iterator 設計優點:隔離出物件儲存、物件遍歷兩項行為

    1. 單一職責:一個類負責一個責任;Iterator 類就是負責遍歷容器內的元素

    2. 界面隔離:最小化界面,不讓容器負擔遍歷的責任

  • Iterator 設計缺點

    1. 非必要性:其最大的缺點其實就是大多數語言都對各自的容器內實現了 Iterator 的功能,我們幾乎很少有需要自己實現(當然除非一些特殊狀況或需求)

    2. 複雜化:自己時現實的複雜化,因為類的增加使用者使用起來會稍加複雜

Iterator 實現

Iterator 標準

  1. Iterator 界面:這裡我們自己配合泛型實現一個 Iterator,有迭代容器的核心方法

    ​​​​interface MyIterator<T> { ​​​​ fun hasNext(): Boolean ​​​​ fun next(): T ​​​​ fun remove() : Boolean ​​​​}
  2. CreteIterator:實做遍歷的方法;在這裡面有 cursor 紀錄使用者對於遍歷的操作

    ​​​​class MyConcreteIterator<T> constructor(val list: MutableList<T>): MyIterator<T> { ​​​​ private var cursor: Int = 0 ​​​​ override fun hasNext(): Boolean { ​​​​ return cursor != list.size ​​​​ } ​​​​ override fun next(): T { ​​​​ // 移動指標 ​​​​ return list[cursor++] ​​​​ } ​​​​ override fun remove(): Boolean { ​​​​ // 移動指標 ​​​​ list.removeAt(cursor++) ​​​​ return true ​​​​ } ​​​​}
  3. Aggregate 界面:提供使用者操控該容器的方法(add, remove),但不提供直接遍歷容器的方法,改成提供 MyIterator 類

    ​​​​interface MyAggregate<T> { ​​​​ fun add(item: T) ​​​​ fun remove(item: T) ​​​​ // 關聯 MyIterator ​​​​ fun createIterator() : MyIterator<T> ​​​​}
  4. ConcreteAggregate:它是一個容器類,保存數據並提供使用者操作數據的方法,但並不包括提供直接遍歷數據的方法(實做 MyIterator)

    這個類負責串起 Iterator 的關係

    ​​​​class ConcreteAggregator: MyAggregate<String> { ​​​​ private val list = mutableListOf<String>() ​​​​ override fun add(item: String) { ​​​​ list.add(item) ​​​​ } ​​​​ override fun remove(item: String) { ​​​​ list.remove(item) ​​​​ } ​​​​ override fun createIterator(): MyIterator<String> { ​​​​ // 串起其關係 ​​​​ return MyConcreteIterator(list.toMutableList()) ​​​​ } ​​​​}
    • 為何要使用 list.toMutableList() ?

      這是一種 快照 作法,toMutableList() 會複製原本陣列的物件,而不是取得引用,這樣在使用上更安全,因為 Iterator 是取用副本的操作

      ​​​​​​​​public fun <T> Collection<T>.toMutableList(): MutableList<T> { ​​​​​​​​ return ArrayList(this) ​​​​​​​​}
  5. 使用方式:以往是直接透過容器遍歷,現在改用取得 MyIterator 來遍歷

    • createIterator 方法是返回一個遍歷的對象,而不是容器內部持有對象,所以請不要在遍歷的過程中使用

      ​​​​​​​​// 以下使用嚴重錯誤! ​​​​​​​​while (createIterator().hasNext()) { ​​​​​​​​ println("item: ${createIterator().next()}") ​​​​​​​​}
    ​​​​fun main() { ​​​​ ConcreteAggregator().apply { ​​​​ add("Hello") ​​​​ add("World") ​​​​ add("Iterator") ​​​​ add("Design") ​​​​ }.run { ​​​​ val iterator = createIterator() ​​​​ while(iterator.hasNext()) { ​​​​ println("item: ${iterator.next()}") ​​​​ } ​​​​ } ​​​​}

Java

Iterator & Iterable 差異

接口 說明 / 功能
Iterable 從這個界面繼承的類可以表示為可以迭代的元素序列
Iterator 可以表示為元素序列的集合或另一個實體的迭代器;允許順序訪問元素

更多的物件導向設計

物件導向的設計基礎如下,如果是初學者或是不熟悉的各位,建議可以從這些基礎開始認識,打好基底才能走個更穩(在學習的時候也需要不斷回頭看)!

創建模式 - Creation Patterns

行為模式 - Behavioral Patterns

結構模式 - Structural Patterns

Appendix & FAQ

tags: Java 設計模式 基礎進階