Swiftで試すデザインパターン - ArrayやDictionaryを支えるIteratorパターン

# 教科書：Java言語で学ぶデザインパターン入門

「数学ガール」で有名な結城浩先生の「Java言語で学ぶデザインパターン入門[^1]」を購入しました[^2]。
せっかくの機会なので、普段使っているSwiftで実装しながら、勉強ノートを作っていこうと思います。
本記事は「第１章 Iterator １つ１つ数え上げる」を参考に作成しました。

# Iteratorパターンの嬉しさがなんとなくわかってきた！

はじめてIteratorパターンに触れたとき（JavaScriptのIteratorでした）は、何が嬉しいのかまったくわかりませんでした。
今回、自分で手を動かして具体的に実装したり、Swiftの`Sequence`や`map`のソースコードを眺めているうちにIteratorパターンの嬉しさがなんとなく実感できた気がします。
同じような気持ちを抱いている方の参考になると嬉しいです！

# Iteratorパターンとは？

ArrayやDictionaryのように、何かの集まりの全体を順番にスキャンしていくときに使われるデザインパターンです。
少し長い引用になりますが、「Java言語で学ぶデザインパターン入門[^1]」では以下のように説明されています。

> for文のi++でiを1ずつ増加させていくと……配列arrの要素全体を最初から順番にスキャン（走査）していることになります。ここで使われている変数iの働きを抽象化し、一般化したものを、デザインパターンでは、
>　　Iteratorパターン
>と呼んでいます。
> Iteratorパターンとは、何かがたくさん集まっているときに、それを順番に指し示していき、全体をスキャンしていく処理を行うためのものです。iterate（イテレート）という英単語は何かを「繰り返す」という意味です。[^3]

# Iteratorパターンの登場人物

## Iteratorインターフェース

要素のスキャンを実行する型が持つインターフェースです。
いろいろな流派があるそうですが、シンプルなものとして、以下のようなprotocolが考えられます。

```swift
protocol MyIteratorProtocol {
    // 配列などが保持する要素の型
    associatedtype Element
    // 次の要素があるかどうかを返すメソッド
    func hasNext() -> Bool
    // スキャン中に次の要素を返すメソッド
    mutating func next() -> Element?
}
```

## Aggregate（集合）インターフェース

配列やDictionaryのように「何か」の集まりを保持する型が持つインターフェースで、iteratorを生成するメソッドを持ちます。

```swift
protocol AggregateProtocol {
    // 配列などが保持する要素の型
    associatedtype Element
    // iteratorメソッドが返すIteratorの型、Elementが一致する必要がある
    associatedtype Iterator: MyIteratorProtocol where Iterator.Element == Element
    // iteratorを生成するメソッド
    func iterator() -> Iterator
}
```

## ConcreteIterator / ConcreteAggregate

IteratorインターフェースとAggregateインターフェースを実装（準拠）した具体的な型たちです。

# 具体例：Iteratorパターンを実装してみる

ここでは例として、Bird型の集合を保持するBirdCage型とそのIteratorであるBirdCageIteratorを実装していきます。

## Bird

名前を保持するだけのstructです。

```swift:Bird.swift
struct Bird {
    let name: String
}
```

## BirdCage

Birdの集合を保持するstructを定義します。
AggregateProtocolに準拠するために、associatedTypeのaliasを設定して、iteratorメソッドを実装します。
ここでは簡単のためにBirdの集合自体は配列で保持します。

```swift:BirdCage.swift
struct BirdCage: AggregateProtocol {
    // associatedTypeに具体的な型を設定する
    typealias Element = Bird
    typealias Iterator = BirdCageIterator
    
    // iteratorを返すメソッドを実装する
    func iterator() -> Iterator {
        BirdCageIterator(birds: birds)
    }
    
    // ここでは簡単のためにArrayとして集合を保持する
    private var birds: [Bird] = []
    
    // birdを鳥籠に入れるメソッド
    mutating func put(bird: Bird) {
        birds.append(bird)
    }
    
    // indexを指定してbirdの様子を覗く
    func peek(birdOf index: Int) -> Bird? {
        index < birds.count ? birds[index] : nil
    }
}
```

## BirdCateIterator

BirdCateのIteratorを定義します。
MyIteratorProtocolに準拠するために、associatedTypeのaliasを設定して、hasNext/nextメソッドを実装します。

```swift:BirdCateIterator.swift
struct BirdCageIterator: MyIteratorProtocol {
    // associatedTypeに具体的な型を設定する
    typealias Element = Bird

    // スキャンするための集合を配列で保持する
    private let birds: [Bird]
    private var index: Int = 0
    
    init(birds: [Bird]) {
        self.birds = birds
    }
    
    func hasNext() -> Bool {
        index < birds.count
    }
    
    // メソッドが呼ばれたら、自身のindexをひとつ進める
    mutating func next() -> Bird? {
        let bird = index < birds.count ? birds[index] : nil
        index += 1
        return bird
    }
}
```

# 使ってみる

集合の各要素をprintする関数を定義して呼び出します。
ポイントは、`Aggregate: AggregateProtocol`のように型パラメータを使って関数を定義することです。
このように実装された関数は、Iteratorパターンで実装されている型（`AggregateProtocol`に準拠している型）すべてに対して使うことができます。

```swift
func printElements<Aggregate: AggregateProtocol>(of aggregate: Aggregate) {
    var iterator = aggregate.iterator()
    while iterator.hasNext() {
        let element = iterator.next()
        print(String(describing: element!))
    }
}

var cage = BirdCage()
cage.put(bird: Bird(name: "Budgerigar"))
cage.put(bird: Bird(name: "Cockatiel"))
cage.put(bird: Bird(name: "Parakeet"))
cage.put(bird: Bird(name: "Parrot"))

printElements(of: cage)

// Bird(name: "Budgerigar")
// Bird(name: "Cockatiel")
// Bird(name: "Parakeet")
// Bird(name: "Parrot")
```

# Iteratorパターンの何が嬉しいのか？

## 具体的な実装（の詳細）とは関係なく使用できる

先ほどの`printElements`関数は、具体的な`BirdCage`型に対してではなく、「`AggregateProtocol`に準拠した`Aggregate`一般」に対して実装されています。
そのため、具体的な`BirdCage`や`BirdCageIterator`が、`iterator`や`next`メソッドをどのように実装しているかとは全く関係なく、`printElements`が引数`aggregate: Aggregate`を使うことができます。

### printElementsを便利に使う

この利点を享受しつつ、`printElements`をより便利に使うためには、`printElements`を`AggregateProtocol`のextensionに定義してやります。

```swift:AggregateProtocol.swift
extension AggregateProtocol {
    func printEelements() {
        var iterator = self.iterator()
        while iterator.hasNext() {
            let element = iterator.next()
            print(String(describing: element!))
        }
    }
}

var cage = BirdCage()
cage.put(bird: Bird(name: "Budgerigar"))
cage.put(bird: Bird(name: "Cockatiel"))

// AggregateProtocolに準拠しているすべてのstructからprintElementsを呼ぶことができる！
cage.printElements()
```

# ArrayやDictionary, StringでもIteratorパターンが使われている

## Swift標準のIteratorProtocol

実はSwiftには標準で`IteratorProtocol`というものが用意されています[^4]。

```swift
public protocol IteratorProtocol {
    associatedtype Element
    mutating func next() -> Element?
}
```

## Iteratorパターンで実装された Sequence protocol

そして、ArrayやDictionary, Stringなどの様々なstructが準拠している`Sequence`プロトコルが`IteratorProtocol`を内部で利用しています[^5]。

```swift
public protocol Sequence {
    associatedtype Element
    associatedtype Iterator: IteratorProtocol where Iterator.Element == Element
    __consuming func makeIterator() -> Iterator
    ...
}
```

この部分の定義は、本記事で作成した`AggregateProtocol`そのものです。
まさにこの`Sequence`というプロトコルがIteratorパターンを使って実装されているのです。

## mapの実装にもIteratorが使われている

たとえば、Arrayではお馴染みの`map`メソッドは、実は`Sequence`プロトコルのextensionに実装されています。
そのため、ArrayやRangeはもちろん、DictionaryやStringについても`map`メソッドを呼び出すことができます。

```swift
[1, 2, 3].map { $0 + 1 }
(0...2).map { $0 * 2 }
["key1": 1, "key2": 2].map { (k, i) in i }
"Hello, world!".map { $0.uppercased() }
```

そして、`map`メソッドの内部ではiteratorが使われています[^6]。
ArrayやDictionary, Range, Stringの内部実装やそれぞれに定義されたIteratorの実装はそれぞれに異なるはずですが、`IteratorProtocol`のインターフェースを通じて`map`メソッドをひとつの実装で済ますことができるわけです。

```swift
extension Sequence {
    ...
    @inlinable
    public func map<T>(
      _ transform: (Element) throws -> T
    ) rethrows -> [T] {
        let initialCapacity = underestimatedCount
        var result = ContiguousArray<T>()
        result.reserveCapacity(initialCapacity)

        var iterator = self.makeIterator()

        // Add elements up to the initial capacity without checking for regrowth.
        for _ in 0..<initialCapacity {
            result.append(try transform(iterator.next()!))
        }
        // Add remaining elements, if any.
        while let element = iterator.next() {
            result.append(try transform(element))
        }
        return Array(result)
    }
}
```

# おわりに

Iteratorパターンの嬉しさが実感できたでしょうか？
次回のパターンもお楽しみに！

# 環境

- Xcode 11.6
- Swift 5.2.4

# 参考

- [増補改訂版 Java言語で学ぶデザインパターン入門](https://www.hyuki.com/dp/)
- https://github.com/apple/swift

[^1]: [増補改訂版 Java言語で学ぶデザインパターン入門](https://www.hyuki.com/dp/)
[^2]: 2020年9月1日現在、サマーセール中でkindle版が半額です！
[^3]: 増補改訂版 Java言語で学ぶデザインパターン入門 第１章 Iterator １つ１つ数え上げる
[^4]: https://github.com/apple/swift/blob/8bbffacb28cd6165551f8c99d582d39e4b5a0177/stdlib/public/core/Sequence.swift#L177
[^5]: https://github.com/apple/swift/blob/8bbffacb28cd6165551f8c99d582d39e4b5a0177/stdlib/public/core/Sequence.swift#L325
[^6]: https://github.com/apple/swift/blob/8bbffacb28cd6165551f8c99d582d39e4b5a0177/stdlib/public/core/Sequence.swift#L600