mohuneko’s blog

かんばる駆け出しエンジニアのブログです

【Java】Iteratorパターン 【デザインパターン】

駆け出しエンジニアがデザインパターンをもくもく勉強します

 こんな本で勉強しています🌟

目次

Iteratorパターン

Iteratorパターンとは

  • ある集合体の要素を、順番にアクセスする方法を提供するパターンです

f:id:mohuNeko:20201226195051p:plain

サンプルプログラム

  • Iteratorパターンを使って、本棚の中に本を入れ、その本の名前を順番に表示するプログラムを実装します
  • 各クラスとインターフェースの役割は以下のようになっています
名前 役割
Aggregate 集合体を表すインターフェース
Iterator 数え上げ、スキャンを行うインターフェース
Book 本を表すクラス
BookShelf 本棚を表すクラス(ConcreteAggregate)
BookShelfIterator 本棚をスキャンするクラス(ConcreteIterator)

Aggregateインターフェース

  • iteratorメソッドの宣言のみ行います
  • このメソッドはIteratorを作成するためのものです
  • 集合体を数え上げたい時に、このiteratorメソッドを使って、Iteratorインターフェース実装クラスのインスタンスを生成します
  • iteratorメソッドはBookShelfクラスで実装します
public interface Aggregate {
    public abstract Iterator iterator();
}

Iteratorインターフェース

  • 次の要素が存在するかどうかを調べるhasNextメソッドと、次の要素を得るnextメソッドを宣言します
  • nextメソッドの戻り値はObject 型です。集合体の要素を一個返すようにBookShelfIteratorクラスで実装します
public interface Iterator {
    public abstract boolean hasNext();
    public abstract Object next();
}

Bookクラス

  • 本の名前を得るgetNameメソッドを宣言します
  • 本の名前は、コンストラクタでインスタンス初期化時に引数で指定しています
public class Book {
    private String name;
    public Book(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}

BookShelfクラス

  • 本棚を表現するクラスです
  • このクラスを集合体として扱う為に、Aggregateインターフェースを実装し、iteratorメソッドの実体を記述します
    • return new BookShelfIterator(this); でBookShelfIteratorクラスのインスタンスを生成して返します
  • Bookクラスのbooksという配列をフィールドに持ちます
public class BookShelf implements Aggregate {
    private Book[] books;
    private int last = 0;

    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;
    }

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

BookShelfIteratorクラス

  • 本棚のスキャンを行います
  • Iteratorとして扱う為、Iteratorインターフェースを実装します
  • nextメソッドでindexを++することで、ループ変数を次に進めます
  • BookShelfクラスのインスタンスbookShelfフィールドに保存します
  • 注目している本をindexフィールドに保存します
public class BookShelfIterator implements Iterator {
    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 Object next() {
        Book book = bookShelf.getBookAt(index);
        index++;
        return book;
    }
}

Mainクラスで本棚のスキャンを実行します

  • Iterator it = bookShelf.iterator();では、BookShelfIterator型の変数ではなく、Iterator型の変数に代入しています
import java.util.*;

public class Main {
    public static void main(String[] args) {
        BookShelf bookShelf = new BookShelf(4);  //4冊入る本棚を作成
        bookShelf.appendBook(new Book("Java言語で学ぶデザインパターン入門"));
        bookShelf.appendBook(new Book("独習Java"));
        bookShelf.appendBook(new Book("徹底攻略Java SE 11 Silver問題集"));
        bookShelf.appendBook(new Book("Spring解体新書"));
        Iterator it = bookShelf.iterator(); //本棚をスキャンするためのIteratorインスタンス生成
        while (it.hasNext()) {                    //次の本があるか確認
            Book book = (Book)it.next();  //本を一つずつ調査
            System.out.println(book.getName());
        }
    }
}

Iteratorパターンを使うメリット

  • Main関数のwhileで呼び出すのは、hasNext、nextのIteratorメソッドのみで、BookShelfメソッドは呼び出していません
  • つまり、whileループはBookShelfの実装に依存しない、ことになります
  • よって、BookShelfの実装を変更しても、iteratorメソッドを持っていてhasNextとnextを実装しているインスタンスを返してくれたら(正しくIteratorを返しているので)、Mainメソッドのwhileループは変更しなくても動作する!のです
  • こうすることで、結合度を下げ、クラスを部品として再利用しやすくすることができます

今日のポイント

  • Iteratorパターンは、ある集合体の要素を、順番にアクセスする方法を提供するパターンです
  • 動作を実行するMain関数のwhileループで呼び出すのは、Iteratorメソッドのみで、BookShelfメソッドは呼び出していません
  • これにより、結合度を下げ、クラスを部品として再利用しやすくすることができます

 本日もお疲れ様です😊