[iOS 8] Swiftでデザインパターン No.2 Iterator
Iterator
Iteratorとは
英単語Iterateには以下のような意味があります。
- 繰り返す
- 反復する
Iteratorは日本語で反復子などとも呼ばれます。
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] } }
- 配列の初期化を行っています。配列を構成する要素の型は"Item"です。
- iteratorメソッドを実装しています。初期化メソッドの引数に自身を渡しています。
- メンバ変数の配列にItemクラスのインスタンスを追加しています。
- メンバ変数の配列の要素数を返します。
- メンバ変数の配列から引数で渡されたインデックスの要素を返します。
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 } }
- ConcreteAggregateのインスタンスを保持します。
- 反復用のインデックスを保持します。
- 初期化メソッドです。
- 要素を取得します。
- 次の要素が存在するかを判定します。
Item
// クラス図にはありません。サンプル実行用の補助クラスです class Item { private var _name: String // 1 var name: String { // 2 get { return _name } // 3 } init(name: String) { // 4 _name = name } }
- パブリック変数nameをreadonlyにするために_nameというプライベート変数を作ります。
- パブリック変数nameを宣言しています。
- getterです。ここにset { ~ }とsetterを書かないことにより、パブリック変数nameがreadonlyになります。
- 初期化メソッドです。
実行コード
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は楽しいですね。