デザインパターン: Iteratorパターン -処理を繰り返す-
はじめに
GoFの23のデザインパターンがまとめられている「Java言語で学ぶデザインパターン入門」を読んでアウトプットとしてデザインパターンを1つずつ記事としてアウトプットしていきます。
原則的にJavaで実装コード例などを記述していきますが、気になったことや改善点、感想等ありましたらぜひコメントくださると嬉しいです!
23のパターン一覧はこちらから ※随時更新中
1.Iteratorパターン
2.Adapterパターン
3.Template Methodパターン
4.Factory Methodパターン
5.Singletonパターン
6.Prototypeパターン
7.Builderパターン
8.Abstract Factoryパターン
9.Bridgeパターン
10.Strategy
11.Compositeパターン
12.Decoratorパターン
13.Visitorパターン
14.Chain of Responsibilityパターン
15.Facadeパターン
16.Mediatorパターン
17.Observerパターン
18.Mementoパターン
19.Stateパターン
20.Flyweightパターン
21.Proxyパターン
22.Commandパターン
23.Interpreterパターン
Iteratorパターンとは
「Iterate」とは日本語で何かを繰り返す、反復すると言う意味で、オブジェクトの集合体(例えばリストや配列など)を操作するためのデザインパターンです。
集合体の内部構造と、集合体へのアクセス方法を分離することで集合体の内部構造が変更されても、Iteratorのインターフェースを変更することなく、外部からのアクセスを維持することができます。
Iteratorパターンのクラス図
Iteratorパターンを構成する4つのクラス
- Aggregate(集約)クラス
Iteratorインスタンスを生成するメソッドを宣言するためのインターフェースを定義します。 - ConcreteAggregate(具体的な集約)クラス
Aggregateクラスで定義したインターフェースを実装し、Iteratorインスタンスを生成する具体的なメソッドを実装します。 - Iterator(反復子)クラス
要素を1つずつ順番に取り出すためのインターフェースを定義します。 - ConcreteIterator(具体的な反復子)クラス
Iteratorクラスで定義したインターフェースを実装し、実際に要素を1つずつ順番に取り出す具体的なメソッドを実装します。
Iteratorパターンを利用するメリット
Iteratorパターンを利用することで以下のようなメリットが得られます。
- 集合体の具体的な構造を気にする必要がなくなり、コードがよりシンプルで読みやすくなります。
- Iteratorが外部に公開されるインターフェースとなり、集合体の実装の詳細を隠蔽することができます。
- 異なる種類の集合体を同じ方法で走査できます。例えば配列、リスト、マップなどさまざまな種類の集合体を走査する際に、同じIteratorインターフェースを再利用できます。
Javaでの実装例
実装のイメージ図
使用するクラス及びインターフェース
名前 | 解説 |
---|---|
Iterable<E> | 集合体を表すインターフェース(java.langパッケージ) 実装例ではIterable<Book>として使用 |
Iterator<E> | 処理を繰り返すインターフェース(java.utilパッケージ) 実装例では Iterator <Book>として使用 |
Book | 本を表すクラス |
BookShelf | 本棚を表すクラス |
BookShelfIterator | 本棚をスキャンするクラス |
Main | 動作確認用のクラス |
public interface Iterable<E> {
public abstract Iterator<E> iterator();
}
public interface Iterator<E> {
public abstract boolean hasNext();
public abstract E next();
}
ここで出てくる2つのメソッドの役割は
1.hasNext()メソッド
次の要素が存在するかを調べる。
2.next()メソッド
次の要素を返す。
ここで一つ注意なのがnext()メソッドは「next」と言う名前に惑わされがちなのですが、戻り値で返すのは現在の要素です。
public class Book {
private String naem;
public setBook(String name) {
this.name = name;
}
public void getName() {
return name;
}
}
Bookクラスは文字通り本を表すクラス。
import java.util.Iterator;
public class BookShelf implements Iterable<Book> {
private Book[] book;
private int last = 0;
@Override
public Iterator<Book> iterator() {
return new BookShelfIterator(this);
}
public BookShelf(int maxsize) {
this.books = new Book[maxsize];
}
public Book getBookAt(int index) {
return books[index];
}
public void appendBook(Book book) {
this.books[last] = book;
last++;
}
public int getLength() {
return last;
}
}
BookShelfクラスは本棚を表すクラスで、Iterable<Book>インターフェースを実装しています。
import java.util.Iterator;
import java.util.NoSuchElementException;
public class BookShelfIterator implements Iterator<Book> {
private BookShelf bookShelf;
private int index;
public BookShelfIterator(BookShelf bookShelf) {
this.bookShelf = bookShelf;
this.index = 0;
}
@Override
public boolean hasNext() {
if (index < bookShelf.getLength()) {
return true;
} else {
return false;
}
}
@Override
public Book next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
Book book = bookShelf.getBookAt(index);
index++;
return book;
}
}
}
BookShelfIteratorはIteartor<Book>インターフェースを実装しており、BookShelfクラスのスキャンを行います。
import java.util.Iterator;
public class Main {
public static void main(String args[]) {
BookShelf bookShelf = new BookShelf(4);
bookShelf.appendBook(new Book("いかついJava"));
bookShelf.appendBook(new Book("かんたんなJava"));
bookShelf.appendBook(new Book("むずかしいJava"));
bookShelf.appendBook(new Book("やさしいJava"));
// 明示定期にIteratorを使う方法
Iteartor<Book> it = bookShelf.iterator();
while(it.hasNext()) {
Book book = it.next();
System.out.println(book.getName());
}
System.out.println();
// 拡張for文を使う方法
for (Book book: bookShelf) {
System.out.println(book.getName())
}
System.out.println();
}
}
なぜfor文ではなくIteratorパターンを使うのか
実装がどうであれ、Iteratorを使える
なぜIteratorパターンと言う面倒なものを考えるか、配列を使っているならfor文でくるくる回せばいいのではないか。集約オブジェクトの外にわざわざIterator役を作る必要があるのか。
大きな理由は、Iteartorパターンを使うことで実装と切り離して繰り返しを使えるからです。
次のコードを見てみましょう
while(it.hasNext()) {
Book book = it.next();
System.out.println(book.getName());
}
ここで使われているのは、hasNextとnextというIteratorのメソッドだけで、BookShelfの実装で使われているメソッドは呼び出されていません。つまりwhileループはBookShelfの実装に依存しないということになります。
これよって、BookShelfの実装を配列からArrayListに変えたとしても上のwhileループは変更しなくても動作します。
デザインパターンはクラスの再利用化を促進するものです。再利用化を促進するとは、クラスを部品として使えるようにすることで1つの部品を修正しても、他の部品の修正が少なく済む、ということです。
まとめ
Iteratorパターンの概要の復習
Iteratorパターンは、集約オブジェクト内部の要素に順番にアクセスする方法を提供し、要素にアクセスする方法を共通化することができます。
このパターンを使用すると、集約オブジェクト内部のデータ構造を隠蔽し、集約オブジェクトの変更に強いコードを書くことができます。
Discussion