[iOS 8] Swiftでデザインパターン No.2 Iterator

2014.09.18

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Iterator

Iteratorとは

英単語Iterateには以下のような意味があります。

  • 繰り返す
  • 反復する

Iteratorは日本語で反復子などとも呼ばれます。

Iteratorパターンは、要素の集まりを保有するオブジェクトの各要素に順番にアクセスする方法を提供するためのパターンです。

クラス図

class_diagram_iterator

ポイント

  • Aggregate, Iteratorクラスは共に抽象クラス
  • 具象クラスはそれぞれConcreteAggregate, ConcreteIteratorクラス
  • AggregateクラスがiteratorメソッドでIteratorクラスのインスタンスを生成する

※ Aggregate = 集合体 / 集団, Concrete = 具体化する / 実体のある

サンプルコード

Aggregate

class Aggregate {
    func iterator() -> Iterator {
        fatalError("must be overridden")
    }
}

Aggregateは抽象クラスということで、メソッドが呼ばれたらfatalErrorを実行し、プログラムが終了するようにしています。
このメソッドはサブクラスで実装されることを想定しています。

Iterator

class Iterator {
    func next() -> AnyObject {
        fatalError("must be overridden")
    }
    func hasNext() -> Bool {
        fatalError("must be overridden")
    }
}

こちらも同様に抽象クラスとなっています。

ConcreteAggregate

class ConcreteAggregate: Aggregate {
    private var items = [Item]()            // 1

    override func iterator() -> Iterator {  // 2
        return ConcreteIterator(aggregate: self)
    }

    func addItem(item: Item) {              // 3
        items.append(item)
    }

    func countOfItems() -> Int {            // 4
        return items.count
    }

    func itemAtIndex(index: Int) -> Item {  // 5
        return items[index]
    }
}
  1. 配列の初期化を行っています。配列を構成する要素の型は"Item"です。
  2. iteratorメソッドを実装しています。初期化メソッドの引数に自身を渡しています。
  3. メンバ変数の配列にItemクラスのインスタンスを追加しています。
  4. メンバ変数の配列の要素数を返します。
  5. メンバ変数の配列から引数で渡されたインデックスの要素を返します。

ConcreteIterator

class ConcreteIterator: Iterator {
    private var aggregate: ConcreteAggregate    // 1
    private var index = 0                       // 2

    init(aggregate: ConcreteAggregate) {        // 3
        self.aggregate = aggregate
    }

    override func next() -> AnyObject {         // 4
        return aggregate.itemAtIndex(index++)
    }

    override func hasNext() -> Bool {           // 5
        return aggregate.countOfItems() > index
    }
}
  1. ConcreteAggregateのインスタンスを保持します。
  2. 反復用のインデックスを保持します。
  3. 初期化メソッドです。
  4. 要素を取得します。
  5. 次の要素が存在するかを判定します。

Item

// クラス図にはありません。サンプル実行用の補助クラスです
class Item {
    private var _name: String   // 1

    var name: String {          // 2
        get { return _name }    // 3
    }

    init(name: String) {        // 4
        _name = name
    }
}
  1. パブリック変数nameをreadonlyにするために_nameというプライベート変数を作ります。
  2. パブリック変数nameを宣言しています。
  3. getterです。ここにset { ~ }とsetterを書かないことにより、パブリック変数nameがreadonlyになります。
  4. 初期化メソッドです。

実行コード

var ca = ConcreteAggregate()
ca.addItem(Item(name: "Item 1"))
ca.addItem(Item(name: "Item 2"))
ca.addItem(Item(name: "Item 3"))
ca.addItem(Item(name: "Item 4"))
ca.addItem(Item(name: "Item 5"))

let it = ca.iterator()

while it.hasNext() {
    let item = it.next() as Item
    println(item.name)
}

実行結果

Item 1
Item 2
Item 3
Item 4
Item 5

まとめ

  • 強制的にプログラムを停止したいときはfatalErrorを使う
  • 配列の初期化はvar 変数名 = [型]()と記述する
  • メソッドをオーバーライドするときはoverrideキーワードを付ける
  • getter, setterはget { ~ }, set { ~ }で記述できる

おまけ

Swiftならではの書き方

クラス図通りには書きましたが、Objective-Cの時代から"要素の集まりを保有するオブジェクトの各要素に順番にアクセスする方法"というものは存在します。
Swiftではそれらは以下のように書くことができます。

for 要素 in 集合 {
    // 要素に対する処理
}

Array

// 実行コード
let names = ["Anakin", "Obi-Wan", "Yoda"]
for name in names {
    println(name)
}

// 実行結果
Anakin
Obi-Wan
Yoda

Dictionary

// 実行コード
let episodes = [
    "1": "The Phantom Menace",
    "2": "Attack of the Clones",
    "3": "Revenge of the Sith"
]
for (key, value) in episodes {
    println("key = (key)")
    println("value = (value)")
}

// 実行結果
key = 2
value = Attack of the Clones
key = 3
value = Revenge of the Sith
key = 1
value = The Phantom Menace

※ Dictionaryのfor-inは順序が保証されないので注意!

String

// 実行コード
let string = "Death☆Star"
for character in string {
    println(character)
}

// 実行結果
D
e
a
t
h
☆
S
t
a
r

以上です。Swiftは楽しいですね。