📝

# デザインパターンを学ぶ #23 イテレーター(Iterator)

に公開

1. はじめに

今回は Iterator(イテレーター)パターン
目的は、集合体の要素へ順番にアクセスする仕組みを統一することです。

典型的な用途は、配列・リスト・ツリーなど、内部構造に依存せずに要素を順に取り出したいときです。
コレクションの種類が増えても「使う側のコードは同じ操作で済む」のが強みです。

2. Iteratorとは?

イテレーターは、コレクションの走査処理をオブジェクトに切り出したものです。

登場役は以下の2つが中心です:

  • Iterator … 要素を順に取り出すためのインターフェース(hasNext(), next() など)
  • Aggregate(集合体) … 自分専用のイテレーターを返すメソッドを持つ

これにより「どのように走査するか(内部構造)」を隠蔽しつつ、共通の方法で扱えます。

3. 実装イメージ(PHP)

<?php
// Iterator インターフェース
interface IteratorInterface {
    public function hasNext(): bool;
    public function next();
}

// 集合体インターフェース
interface Aggregate {
    public function createIterator(): IteratorInterface;
}

// 具体的な集合体(本棚)
class BookShelf implements Aggregate {
    private array $books = [];

    public function addBook(string $book): void {
        $this->books[] = $book;
    }

    public function getBookAt(int $index): string {
        return $this->books[$index];
    }

    public function count(): int {
        return count($this->books);
    }

    public function createIterator(): IteratorInterface {
        return new BookShelfIterator($this);
    }
}

// 具体的なイテレーター
class BookShelfIterator implements IteratorInterface {
    private BookShelf $bookShelf;
    private int $index = 0;

    public function __construct(BookShelf $bookShelf) {
        $this->bookShelf = $bookShelf;
    }

    public function hasNext(): bool {
        return $this->index < $this->bookShelf->count();
    }

    public function next(): string {
        return $this->bookShelf->getBookAt($this->index++);
    }
}

// --- 実行例 ---
$bookShelf = new BookShelf();
$bookShelf->addBook("Design Patterns");
$bookShelf->addBook("Refactoring");
$bookShelf->addBook("Clean Code");

$it = $bookShelf->createIterator();
while ($it->hasNext()) {
    echo $it->next() . PHP_EOL;
}
// 出力:
// Design Patterns
// Refactoring
// Clean Code

4. メリット・デメリット

メリット

  • コレクションの内部構造を隠蔽できる
  • 配列・リスト・ツリーなど異なる構造でも同じ方法で走査できる
  • 複数の走査方法(順方向・逆方向)を追加しやすい

デメリット

  • 実装クラスが増える
  • PHPの foreachIteratorAggregate が既に同様の仕組みを持つため、自作は冗長になることもある

5. 使いどころ

  • 独自コレクションを実装するとき(例えばドメイン固有のリスト)
  • 異なるデータ構造を同じ操作で扱いたいとき
  • 走査処理を外に漏らしたくないとき

実際のPHP開発では SPL(Standard PHP Library)の IteratorIteratorAggregate を使うことが多く、フレームワークのコレクション実装でもこのパターンが応用されています。

Discussion