第2章


Iteratorパターン(1つ1つ数え上げる)

「Iterator」とは?

配列などの要素を1つ1つ数え上げるときに使用する。

配列aryの要素を全て表示するには、for文を使って以下のように表現できる

$ary_length = count(ary);
for ($i = 0; i < $arr_length - 1; $i++) {
    print $ary[$i];
}

ここで使われている変数iの働きを抽象化して、一般化したものを
Iteratorパターンという

Iteratorの具体例 (サンプルプログラム)

「本棚(BookShelf?)の中に本(Book)を入れ、その本の名前を順番に表示する」
プログラムを考えてみる

Aggregateインターフェイス

<?php

interface Aggregate {
    function iterator_method();
}

?>

Iteratorインターフェイス

<?php

interface IteratorClass {
    function hasNext();
    function next();
}

?>

Bookクラス

<?php

class Book {

    private $book_name;

    public function __construct($input_book_name) {
        $this->book_name = $input_book_name;
    }

    public function getName() {
        return $this->book_name;
    }
}

?>

BookShelf?クラス

<?php

require_once("Aggregate.class.php");
require_once("BookShelfIterator.class.php");

class BookShelf implements Aggregate {

    private $books;
    private $last;

    public function __construct($maxsize) {
        $this->books = array_pad(array(), $maxsize, 0);
        $this->last  = 0;
    }

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

    public function appendBook($book) {
        $this->books[$this->last] = $book;
        $this->last++;
    }

    public function getLength() {
        return $this->last;
    }

    public function iterator_method() {
        return new BookShelfIterator($this);
    }
}

?>

BookShelfIterator?クラス

<?php

require_once("IteratorClass.class.php");

class BookShelfIterator implements IteratorClass{

    private $book_shelf_iterator;
    private $index;

    public function __construct($book_shelf) {
        $this->book_shelf_iterator = $book_shelf;
        $this->index = 0;
    }

    public function hasNext() {
        if ($this->index < $this->book_shelf_iterator->getLength()) {
            return true;
        } else {
            return false;
        }
    }

    public function next() {
        $book = $this->book_shelf_iterator->getBookAt($this->index);
        $this->index++;
        return $book;
    }
}

?>

Mainクラス

<?php

require_once("BookShelf.class.php");
require_once("Book.class.php");

class Main {

    public function __construct() {
        $book_shelf = new BookShelf(4);
        $book_shelf->appendBook(new Book("Around the World in 80 Days"));
        $book_shelf->appendBook(new Book("Bible"));
        $book_shelf->appendBook(new Book("Cinderella"));
        $book_shelf->appendBook(new Book("Daddy-Long-Legs"));

        $book_shelf_iterator = $book_shelf->iterator_method();

        while ($book_shelf_iterator->hasNext()) {

            $book = $book_shelf_iterator->next();
            echo $book->getName()."<br />";
        }
    }
}

new Main();

?>

Iteratorパターンにでてくるもの


Iterator(反復子)

要素を順番に数え上げていく役。
サンプルプログラムではIteratorClass?クラスにあたる。

ConcreteIterator?(具体的な反復子)

Iterator役が指定したものを、実際に実装する役。
この役はスキャンするために必要な情報を持っている必要がある。
サンプルプログラムではBookShelfIterator?クラスにあたる。

Aggregate(集合体)

Iterator役を作り出すものを定めてくれる役。
サンプルプログラムではAggregateインターフェイスにあたる。

ConcreteAggregate?(具体的な集合体)

Aggregate役が定めたものを実際に実装する役。
具体的なIterator役、つまりConcreteIterator?役のインスタンスを作り出す。
サンプルプログラムではBookShelf?:qクラスにあたる。

Iteratorパターンの利点


実装と切り離して数え上げを行なうことが出来る

while (it.hasNext) {
    Book book = (Book)it.next();
    System.out.println(book.getName());
}

ここで使っているのは、hasNextとnextというIteratorのメソッドだけ。
BookShelf?の実装で使われているメソッドは使用していない。
つまり、このwhileループは、BookShelf?の実装には依存しない

BookShelf?を実装した人が、配列を使って本を管理することをやめて、
別の方法で管理したとしても、
BookShelf?がiteratorメソッドを持っていて、
正しいIteratorを返してくれれば、
(つまり、hasNextとnextメソッドが正しく実装されているクラスのインスタンスを返してくれれば)
上記のwhileループはまったく変更しなくても動作する

関連しているパターン

Visitorパターン

Iteratorは集合体から1つずつ要素を取り出して数え上げていく。
数え上げていくには、要素に対してなんらかの処理が必要だが、
Iteratorのインターフェイスの中にはその処理は記述されていない。

Visitorはたくさんの要素が集まっている中を歩きわたりながら、
同じ処理を繰り返し適用していくというもの。

数えながら、同時に定型処理を行っていく。

そのときに、IteratorとVisitorを使う。

Factory Methodパターン

iteratorメソッドがIteratorのインスタンスを作成するときに、
Factory Methodパターンが使われる場合がある


参考資料

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