Menu
Coddy logo textTech

イテレーターパターン

CoddyのJavaジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 76/87。

Iteratorパターンは、基盤となる構造を公開することなく、コレクションの要素に順番にアクセスする方法を提供する振る舞い設計パターンです。array、連結リスト、またはツリーのいずれを扱っている場合でも、iteratorによって要素を走査するための統一された方法が得られます。

このパターンでは、走査のロジックをコレクション自体から分離します。主に2つのコンポーネントで構成されます。要素を走査するためのメソッドを定義する Iterator interface と、そのコレクション用のイテレーターを作成する Iterable(または Aggregate)です。

interface Iterator<T> {
    boolean hasNext();
    T next();
}

interface Container<T> {
    Iterator<T> createIterator();
}

class BookShelf implements Container<String> {
    private String[] books;
    private int count = 0;
    
    public BookShelf(int size) {
        books = new String[size];
    }
    
    public void addBook(String book) {
        books[count++] = book;
    }
    
    public Iterator<String> createIterator() {
        return new BookIterator();
    }
    
    private class BookIterator implements Iterator<String> {
        private int index = 0;
        
        public boolean hasNext() {
            return index < count;
        }
        
        public String next() {
            return books[index++];
        }
    }
}

iterator は独自の走査状態を保持するため、複数の iterator が同じコレクションをそれぞれ独立して走査できます。クライアントは、コレクションがデータをどのように格納しているかを知らなくても iterator を使用できます。

BookShelf shelf = new BookShelf(3);
shelf.addBook("Design Patterns");
shelf.addBook("Clean Code");

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

Javaに組み込まれているIterableおよびIteratorインターフェースは、この正確なパターンに従っています。そのため、Iterableをimplementsする任意のクラスで拡張forループを使用できます。Iteratorパターンは、複数の走査方法を提供したり、複雑な内部構造をクライアントコードから隠したりする必要がある場合に不可欠です。

challenge icon

チャレンジ

簡単

Iterator Pattern を使ってプレイリストシステムを構築しましょう!曲を保存し、それらを順番に走査するためのカスタム iterator を提供する音楽プレイリストを作成します。曲が内部でどのように保存されているかを公開する必要はありません。これは Iterator Pattern の最適な使用例であり、ユーザーはクリーンで統一された interface を通じてコレクションを操作できます。

コードを4つのファイルに整理します。

  • Iterator.java: 汎用的な Iterator<T> interface を Define し、2つのメソッドを用意します。hasNext() は、さらに要素が exist するかを示す boolean を返し、next() は型 T の次の要素を返します。
  • Playlist.java: songs を holds する Aggregate class を Create します。Playlist は、固定 capacity の String array(constructor に渡されます)に songs を保存し、何曲が added されたかを追跡します。プレイリストに songs を追加する addSong(String song) メソッドを含めます。

    Playlist には、Iterator<String> を返す createIterator() メソッドが必要です。これを、PlaylistIterator という名前の private な inner class を Create し、この class に Iterator interface を implements させることで実装します。この inner iterator は独自の index 位置を保持し、songs array を走査して、each song を順番に返します。

  • Song.java: song のタイトルをラップするシンプルな Song class を Create します。タイトル(String)を受け取る constructor、getTitle() メソッド、および Playing: [title] を返す toString() メソッドを持つ必要があります。
  • Main.java: iterator システムをまとめて動作させます!input として1つの値、つまりカンマ区切りの song タイトルのリスト(例: Bohemian Rhapsody,Stairway to Heaven,Hotel California)を受け取ります。

    capacity が10 songs の Playlist を Create します。input を解析し、each song title をプレイリストに追加します。次にプレイリストから iterator を取得し、それを使って all songs を走査し、それぞれを Song object でラップして出力します(Playing: [title] として表示されます)。

    all songs の iterating が完了した後、新しい行に Playlist complete! を出力します。

input として、カンマ区切りの song titles の文字列を1つ受け取ります。

たとえば、input が Yesterday,Imagine,Let It Be の場合、出力は次のようになります。

Playing: Yesterday
Playing: Imagine
Playing: Let It Be
Playlist complete!

Main class が iterator の hasNext() および next() メソッドを使用して、基盤となる array 構造について何も知らずにプレイリストを走査していることに注目してください。iterator はすべての走査ロジックをカプセル化し、コレクションの内部表現を client code から隠しています。

自分で試してみよう

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String input = scanner.nextLine();
        
        // TODO: 10曲分の容量を持つPlaylistを作成する
        
        // TODO: 入力(カンマ区切り)を解析し、各曲名をplaylistに追加する
        
        // TODO: createIterator()を使用してplaylistからイテレータを取得する
        
        // TODO: イテレータを使用してすべての曲を走査する
        // 各曲をSongオブジェクトでラップして出力する
        
        // TODO: Print "Playlist complete!" after iterating through all songs
    }
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Javaオンラインコンパイラ