デザインパターン[GoF 23]#1 Iteratorパターン

こんにちは!ぐちです。

〜ぼっちAdvent Calendar風イベント実施中[9日目]〜

今回は下記の記事で投稿したデザインパターンの一つ目をご紹介したいと思います。

Iteratorパターンとは

何をするためのデザインパターンなのか。ずばり!!「一つ一つ数え上げる」です。笑 Iteratorというのは繰り返しを意味する、いわゆる反復子のことです。

端的に言うと・・・

ArrayList<String> list = new ArrayList<String>();
// ごにょごにょ
for (String str : list) {
     System.out.println(str);
}

ということです。

これをIteratorパターンに当てはめていきましょう。

クラスとインターフェース

今回は本棚を例に進めていきたいと思います。先ほどのコードでいくとlistに格納されているString型の文字列が本のタイトルのイメージです。

今回、例として挙げる本棚を作るには下記のクラスやインターフェースが必要です。

Aggregate [interface]

集合体を表すインターフェース
※集合体ってなんだよ!というツッコミもあるかと思いますが、まぁそういうことです。笑
Iteratorオブジェクトを返すiterator()メソッドを定義するだけです。

public interface Aggregate {
     public abstract Iterator iterator();
}

Iterator [interface]

Iteratorの中身の定義
具体的な実装はしません。

public interface Iterator {
     public abstract boolean hasNext();

     public abstract Object next();
}

Book[class]

今回の集合体は本棚として設定しているので、それの要素である本を定義します。

public class Book {
     private String title;

     public Book(String _title) {
          this.title = _title;
     }

     public String getTitle() {
          return title;
     }
}

BookShelf[class]

今回の集合体となります。

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

     public BookShelf() {
          books = new ArrayList<Book>();
     }

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

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

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

     @Override
     public Iterator iterator() {
          return new BookShelfIterator(this);
     }
}

BookShelfIterator[class]

具体的な集合体(今回ではBookShelf)の反復子を実装します。

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

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

     @Override
     public boolean hasNext() {
          return (index < bookShelf.getLength());
     }

     @Override
     public Object next() {
          return bookShelf.getBookAt(index++);
     }
}

Main[class]

テスト動作用の実行クラスです。

public class Main {
     public static void main(String[] args) {
          BookShelf bookShelf = new BookShelf();
          // ごにょごにょ
          Iterator it = bookShelf.iterator();
          while (it.hasNext()) {
               Book book = (Book) it.next();
               System.out.println(book.getTitle());
          }
     }
}

なぜこんなに面倒なことを?

一番のメリットは修正箇所を局所化することができることです。Iteratorパターンを使っていれば、中身を全部表示するコードは下記がすべてです。

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

で、これが何がいいのかといいますと、「逆順で数えたい」とか「ランダムで表示したい」とか方向性が変わる(または追加になる)際に、Iteratorパターンを使っていなければ、それぞれに適したfor文を書く必要が出てきますが、Iteratorパターンを使っていれば、BookShelfReverseIteratorクラスやBookShelfRandomIteratorクラスを作るだけ、その他の部分は全く更新がいりません。内部の処理の変更が呼び元に影響しないというのはかなり大きなメリットがあります。

ポイント

上記のIteratorパターンにはもうひとつポイントがありまして、

Iterator it = bookShelf.iterator();

上記の構文であれば、Iteratorインターフェースさえ実装していれば何でも列挙できるのが特徴です。いわゆるポリモーフィズムの考え方が見事に実装されているということになります。

さて、1/23個目のデザインパターンのご紹介でしたが、いかがでしたか?このボリュームが続くことは考えにくいですが、残り22個も頑張って紹介していきます!

サンプルコード

GitHubにアップしていますのでご自由にお使いください。

では、今回はこの辺で。

2016年振り返り

夏に初めてスキューバダイビングをしました。体験レッスンだけでしたのでライセンス取得まではいかなかったのですが、ずっとやってみたいと思っていたので嬉しかったです。もともと競技スポーツが好きな性格でして、ダイビングはハマらないだろうなと思っていたのですが、これが予想外におもしろいんです。
普段は絶対に体感できない驚きの数々で、本当に楽しめました。残念ながら今年は一回しか行けなかったのですが、来年はまたチャレンジしてみたいと思います。

ぼっちAdvent Calendar風ルール

  • 1日1記事更新する
  • 記事内に1年間の出来事の振り返りを1つ明記する
  • 1日の期間は起きてから寝るまで(時刻ではない)

ぼっちAdvent Calendar風イベントとは?下記の記事をご覧ください。