Kotlin
Iterator
デザインパターン

Kotlinでデザインパターン Iterator編

はじめに

本記事はJava言語で学ぶデザインパターン入門を見ながら
JavaではなくKotlinで実装してみようというものです。

Java言語で学ぶデザインパターン入門

調べてみる

まずはIteratorとは何かというところから調べましょう。

WikipediaではIteratorについて次のように解説されています。

プログラミング言語において配列やそれに類似する集合的データ構造の
各要素に対する繰り返し処理の抽象化である。

配列や類似する集合的データ構造の各要素に対する繰り返し処理の抽象化です。
つまりは配列・リスト・独自データ構造などのデータの集まりでも
同じインタフェースで繰り返し処理ができるようにすることがIteratorの役目ということですね。

実装してみる

Iterator

ある集合体クラスを繰り返すためのインタフェースを定義します。

interface Iterator {
    fun hasNext() : Boolean
    fun next() : Object
}

Aggregate

集合体クラスを表すインタフェースを定義します。

interface Aggregate {
    fun iterator() : Iterator
}

Book

集合体クラスの各項目をBookクラスとして定義します。

data class Book(val name : String)

BookShelf

集合体クラスとしてBookShelfを定義
BookShelfは集合体クラスとしたいのでAggregateを実装します。
Aggregateを実装するとIteratorを取得できるようになりますので、
BookShelfの各要素に対して繰り返し処理を行うことができるようになります。

class BookShelf(maxsize : Int) : Aggregate {
    private val books : Array<Book?> = arrayOfNulls(maxsize)
    private var last : Int = 0

    val length : Int get() = last

    fun getBookAt(index : Int) : Book? {
        return books[index]
    }

    fun appendBook(book : Book) {
        this.books[last] = book
        last++
    }

    override fun iterator(): Iterator {
        return BookShelfIterator(this)
    }
}

BookShelfIterator

BookShelfのためのIteratorをBookShelfIteratorとして定義
集合体クラスによって配列の要素を取得・追加するメソッドは異なります。
次のように集合体クラスごとにIteratorを実装したクラスを作成しインタフェースを揃えてやります。

class BookShelfIterator(bookShelf : BookShelf) : Iterator{
    private val bookShelf : BookShelf = bookShelf
    private var index : Int = 0

    override fun hasNext(): Boolean = (index < bookShelf.length)
    override fun next(): Object {
        val book = bookShelf.getBookAt(index)
        index++
        return book as Object
    }
}

Main

上記で定義したクラスとインタフェースを使って、
集合体の各項目に対して繰り返し処理を実行します。

fun main(args : Array<String>) {
    val bookShelf : BookShelf = BookShelf(4)
    bookShelf.appendBook(Book("Around the World in 80 Days"))
    bookShelf.appendBook(Book("Bible"))
    bookShelf.appendBook(Book("Cinderella"))
    bookShelf.appendBook(Book("Daddy-Long-Legs"))

    val iterator : Iterator = bookShelf.iterator()
    while (iterator.hasNext()) {
        val book : Book = iterator.next() as Book
        println(book.name)
    }
}

次のようにFor文でぐるぐるまわすパターンだと、
配列やリストなど集合体を表すパターンごとに繰り返し処理を変えなければなりません。
これをなくすためのデザインパターンがIteratorなんですね。

    val array : Array<Book?> = arrayOfNulls(4)
    array[0] = Book("Around the World in 80 Days")
    array[1] = Book("Bible")
    array[2] = Book("Cinderalla")
    array[3] = Book("Daddy-Long-Legs")
    for(i in 0..3){
        // Indexでアクセスしたり、またはクラス固有のメソッドを呼び出したり
        println(array[i]?.name)
    }

Iteratorは自然に触れているデザインパターンですよね、
ForEachとか処理できるのもIteratorを実装しているからなわけですしね。