🤣

デザインパターン入門Iteratorパターンについて

2023/03/19に公開

0. TL;DR用

Iteratorパターン

public interface Aggregate<T> {
    public Iterator<T> iterator();
}
public class BookShelf implements Aggregate<Book> {
    private List<Book> books;

    public BookShelf(int maxsize) {
        this.books = new ArrayList<Book>(maxsize);
    }

    public Book getBookAt(int index) {
        return books.get(index);
    }

    public void appendBook(Book book) {
        this.books.add(book);
    }

    public int getLength() {
        return books.size();
    }

    public Iterator<Book> iterator() {
        return new BookShelfIterator(this);
    }
}
public interface Iterator<T> {
    public boolean hasNext();
    public T next();
}
public class BookShelfIterator implements Iterator<Book> {
    private BookShelf bookShelf;
    private int index;

    public BookShelfIterator(BookShelf bookShelf) {
        this.bookShelf = bookShelf;
        this.index = 0;
    }

    public boolean hasNext() {
        if (index < bookShelf.getLength()) {
            return true;
        } else {
            return false;
        }
    }

    public Book next() {
        Book book = bookShelf.getBookAt(index);
        index++;
        return book;
    }
}

1. Iteratorパターンとは

Iteratorパターンの概要

Iteratorパターンとは、集合体の要素を順番にアクセスするための方法を提供するパターンです。Iteratorパターンは、集合体とアクセス方法を分離することで、集合体の内部構造に依存せずに要素にアクセスすることができるようにします。Iteratorパターンは、ジェネリックな構造によって要素のアクセスを実現することができます。

Iteratorパターンの利用目的

Iteratorパターンを使用することで、以下のようなメリットが得られます。

  • 集合体の内部構造を知る必要がなく、簡潔にコードを記述することができる。
  • 要素を順番に取り出すことができるため、検索や並び替えなど、要素に対する様々な操作を行いやすくなります。
  • Iteratorパターンを使用することで、複数の集合体で同じIteratorを使用することができるため、コードの再利用性が高まります。

Iteratorパターンは、Javaの標準APIであるCollection Frameworkや、GUIフレームワークのSwingなど、多くの場面で使用されています。

2. Iteratorパターンのクラス図

Iteratorパターンの基本的なクラス図の説明

Iteratorパターンは、反復処理を行うデータ構造において、要素の数や並び順を意識せずに要素にアクセスするためのパターンです。以下は、Iteratorパターンの基本的なクラス図です。

Iteratorパターン

Iteratorパターンは、以下の4つのクラスで構成されます。

  • Aggregate(集約)クラス
    • Iteratorインスタンスを生成するメソッドを宣言するためのインターフェースを定義します。
  • ConcreteAggregate(具体的な集約)クラス
    • Aggregateクラスで定義したインターフェースを実装し、Iteratorインスタンスを生成する具体的なメソッドを実装します。
  • Iterator(反復子)クラス
    • 要素を1つずつ順番に取り出すためのインターフェースを定義します。
  • ConcreteIterator(具体的な反復子)クラス
    • Iteratorクラスで定義したインターフェースを実装し、実際に要素を1つずつ順番に取り出す具体的なメソッドを実装します。

Iteratorパターンにおける各クラスの役割と責任

  1. Aggregate(集合体)
    • 集合体の役割を持つクラスで、Iteratorオブジェクトを作成する責任がある。
    • 具体的には、自分の持つ要素を順番に返すIteratorオブジェクトを作成し、それを返すメソッドを持つ。
  2. Iterator(反復子)
    • 集合体に含まれる要素を順番に取り出す責任を持つクラス。
    • また、要素があるかどうかを調べるhasNextメソッド、要素を取り出すnextメソッド、要素を削除するremoveメソッドを持つ。
  3. ConcreteAggregate(具体的な集合体)
    • Aggregateクラスを実装した具体的なクラス。
    • 自分の持つ要素を順番に返すIteratorオブジェクトを作成し、それを返すメソッドを実装する。
  4. ConcreteIterator(具体的な反復子)
    • Iteratorクラスを実装した具体的なクラス。
    • 集合体に含まれる要素を順番に取り出すnextメソッド、要素があるかどうかを調べるhasNextメソッド、要素を削除するremoveメソッドを実装する。

Iteratorパターンでは、集合体と反復子の責任を明確に分離することで、集合体の内部実装の変更に対しても反復子を変更することなく対応できるようになります。

3. Iteratorパターンの使用例

コレクションを扱う例

Iteratorパターンは、コレクションの要素にアクセスする方法を提供します。例えば、ArrayListやLinkedListなどのコレクションクラスでは、Iteratorを利用して要素にアクセスすることができます。

List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("orange");

Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String fruit = iterator.next();
    System.out.println(fruit);
}

上記の例では、ArrayListに3つの要素が追加され、Iteratorを利用して順次要素にアクセスし、出力しています。

キューを扱う例

Iteratorパターンは、キューの要素にアクセスする方法も提供します。例えば、Queueインタフェースを実装したPriorityQueueクラスでは、Iteratorを利用してキューの要素にアクセスすることができます。

Queue<String> queue = new PriorityQueue<>();
queue.add("apple");
queue.add("banana");
queue.add("orange");

Iterator<String> iterator = queue.iterator();
while (iterator.hasNext()) {
    String fruit = iterator.next();
    System.out.println(fruit);
}

上記の例では、PriorityQueueに3つの要素が追加され、Iteratorを利用して要素にアクセスし、出力しています。

ツリー構造を扱う例

Iteratorパターンは、ツリー構造の要素にアクセスする方法も提供します。例えば、TreeSetクラスでは、Iteratorを利用して要素にアクセスすることができます。

Set<String> set = new TreeSet<>();
set.add("apple");
set.add("banana");
set.add("orange");

Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
    String fruit = iterator.next();
    System.out.println(fruit);
}

上記の例では、TreeSetに3つの要素が追加され、Iteratorを利用して要素にアクセスし、出力しています。

4. Iteratorパターンのメリットとデメリット

Iteratorパターンのメリット

Iteratorパターンには以下のようなメリットがあります。

  • 柔軟性が高い:Iteratorパターンにより、コレクションやデータ構造を隠蔽して、要素にアクセスするための共通インターフェースを提供できます。これにより、コレクションやデータ構造を変更しても、利用者側のコードを変更する必要がなくなります。
  • 複数の要素に同時アクセスできる:Iteratorパターンを利用することで、複数の要素に同時にアクセスできます。これにより、例えばリストの要素を同時に走査して、2つの要素を比較するような処理を実現することができます。

Iteratorパターンのデメリット

Iteratorパターンには以下のようなデメリットがあります。

  • 実装が複雑:Iteratorパターンを実装するには、Iteratorインターフェースを定義して、各コレクションの具象Iteratorクラスを実装する必要があります。また、コレクションやデータ構造を更新した際には、Iteratorも同時に更新する必要があります。
  • パフォーマンスの低下:Iteratorパターンを利用することで、要素にアクセスするためにインスタンスを生成する必要があります。これにより、パフォーマンスが低下する可能性があります。

5. Iteratorパターンと他のデザインパターンとの関係

IteratorパターンとCompositeパターンの関係

Compositeパターンでは、オブジェクトの集合体を扱う際に再帰的な構造を持たせることができます。Iteratorパターンでは、オブジェクトの集合体を順番にアクセスすることができます。これらのパターンは組み合わせて使うことで、集合体を再帰的にたどりながら、全ての要素に対して順次アクセスすることができます。

IteratorパターンとFactory Methodパターンの関係

Factory Methodパターンでは、具体的な生成方法を定義することで、柔軟なインスタンスの生成を実現します。Iteratorパターンでも、コレクションを含むオブジェクトの生成方法を定義することで、Iteratorインスタンスを生成することができます。

IteratorパターンとTemplate Methodパターンの関係

Template Methodパターンでは、スーパークラスでアルゴリズムの骨組みを定義し、サブクラスで具体的な実装を行うことで、処理の共通化を実現します。Iteratorパターンでも、Itratorインターフェースでアルゴリズムの骨組みを定義し、ConcreteIteratorで具体的な実装を行うことで、異なるオブジェクトの集合体を共通的な方法でアクセスすることができます。

6. Iteratorパターンの具体的な実装例

Java言語におけるIteratorパターンの実装方法

  • Aggregate(集約)クラス

Aggregateクラスは、Iteratorパターンにおける集約の役割を担います。集約は、コレクションやリストなどの複数の要素を持つオブジェクトのことです。以下は、Aggregateクラスの例です。

public interface Aggregate<T> {
    public Iterator<T> iterator();
}
  • ConcreteAggregate(具体的な集約)クラス

ConcreteAggregateクラスは、Aggregateクラスの具体的な実装です。以下は、ConcreteAggregateクラスの例です。

public class BookShelf implements Aggregate<Book> {
    private List<Book> books;

    public BookShelf(int maxsize) {
        this.books = new ArrayList<Book>(maxsize);
    }

    public Book getBookAt(int index) {
        return books.get(index);
    }

    public void appendBook(Book book) {
        this.books.add(book);
    }

    public int getLength() {
        return books.size();
    }

    public Iterator<Book> iterator() {
        return new BookShelfIterator(this);
    }
}
  • Iterator(反復子)クラス

Iteratorクラスは、集約に含まれる要素を順番に取り出すためのインターフェースを定義します。以下は、Iteratorクラスの例です。

public interface Iterator<T> {
    public boolean hasNext();
    public T next();
}
  • ConcreteIterator(具体的な反復子)クラス

ConcreteIteratorクラスは、Iteratorクラスの具体的な実装です。以下は、ConcreteIteratorクラスの例です。

public class BookShelfIterator implements Iterator<Book> {
    private BookShelf bookShelf;
    private int index;

    public BookShelfIterator(BookShelf bookShelf) {
        this.bookShelf = bookShelf;
        this.index = 0;
    }

    public boolean hasNext() {
        if (index < bookShelf.getLength()) {
            return true;
        } else {
            return false;
        }
    }

    public Book next() {
        Book book = bookShelf.getBookAt(index);
        index++;
        return book;
    }
}

BookShelfIteratorは、Iteratorインターフェースの実装クラスで、BookShelfクラスが保持しているBookオブジェクトを順番に取得します。

7. まとめ

Iteratorパターンの概要の復習

Iteratorパターンは、集約オブジェクト内部の要素に順番にアクセスする方法を提供するパターンです。Iteratorパターンを利用することで、要素にアクセスする方法を共通化することができます。このパターンを使用すると、集約オブジェクト内部のデータ構造を隠蔽し、集約オブジェクトの変更に強いコードを書くことができます。

Iteratorパターンのメリットとデメリットの復習

Iteratorパターンのメリットは、以下の通りです。

  • 集約オブジェクト内部のデータ構造を隠蔽することができるため、コードの柔軟性が向上します。
  • Iteratorオブジェクトを使用することで、コレクション内部の要素に簡単にアクセスすることができます。
  • Iteratorオブジェクトは、コレクション内の要素を順番に処理するため、効率的な処理が可能です。

Iteratorパターンのデメリットは、以下の通りです。

  • Iteratorオブジェクトは、集約オブジェクトと1対1の関係になるため、多数のIteratorオブジェクトを作成するとオーバーヘッドが発生する場合があります。
  • Iteratorパターンを使用することで、コードの複雑性が増す場合があります。

Discussion