Iteratorパターン実装してみた


どーも、ふぎとです。

今回は結城浩『Java言語で学ぶデザインパターン入門』
(SB Creative)を参考にしながら、デザインパターン
のひとつである「Iteratorパターン」を実装していき
ます。

Iteratorパターンとは

Iteratorパターンとは、何かがたくさん集まっている
ときに、それを順番に指示していき、全体をスキャン
していく処理を行うためのものです。(P.2)

例えていえば、「袋に入ったモノの正体をひとつずつ
調べる」というプログラム、という感じかな。

本書ではサンプルプログラムとして、「本棚の中に入
れた本を調べ、その本の名前を順番に表示する」もの
が紹介されています。今回はそこにアレンジを加えて
ソングリストに入った曲をひとつずつ表示させる
プログラムをIteratorパターンで書いていこうと思いま
す。

Aggregate(集合体)インターフェース

Aggregateインターフェースは、数え上げを
行うものの「集合体」を表します。(P.3)

ここでは「曲の集合体」を表す「SongAggregate」
というインターフェースを作成します。

SongAggregate.java
public interface SongAggregate {
    public abstract SongIterator songIterator();
}

このインターフェースでは、集合体を数えあげる
ためのメソッド「songIterator」のみを宣言します。

Iteratorインターフェース

Iteratorインターフェースは要素の数え上げ
を行うもの、ループ変数のような役割を果た
すものです。

ここでは「SongIterator」を作成します。

SongIterator.java
public interface SongIterator {
    public abstract boolean hasNext();
    public abstract Object next();
}

hasNextメソッドは、「集合体」に次の要素があるか
どうか調べるためのものです。またnextメソッドは
次の要素がある場合に、その要素を得ます。

Songクラス

ひとつひとつの「曲」を表すためのクラスです。

Song.java
public class Song {
    private String songName;

    public Song(String s) {
        this.songName = s;
    }

    public String getSongName() {
        return songName;
    }
}

SongListクラス

「曲の集合体」を表現しているクラスです。
SongAggregateインターフェースを実装し、抽象メソッドで
あったsongIteratorメソッドをオーバーライドします。

SongList.java
public class SongList implements SongAggregate {
    private Song[] songs;
    private int last = 0;

    public SongList(int max) {
        this.songs = new Song[max];
    }

    public Song getSongAt(int id) {
        return songs[id];
    }

    public void appendSong(Song s) {
        this.songs[last] = s;
        last++;
    }

    public int getLength() {
        return last;
    }

    @Override
    public SongIterator songIterator() {
        return new SongListIterator(this);
    }

}

ここではコンストラクタによって、曲を最大max
個まで格納できるSongListインスタンスを定義
しています。
getSongAtメソッドは、id番目の曲を返します。
appendSongメソッドは、配列songs[]の一番後ろに
新たな曲sを格納します。
getLengthは、SongListに入っている曲の数を返す
メソッドです。
そして、オーバーライドしたsongIteratorメソッドでは、
これから作成するSongListIteratorというクラスの
インスタンスを生成し、SongIterator型として返します。

SongListIteratorクラス

実際にSongListクラスの走査(スキャン)を行う
クラスです。SongIteratorインターフェースを実
装し、hasNextメソッド・nextメソッドをオーバー
ライドします。

SongListIterator.java
public class SongListIterator implements SongIterator {
    private SongList songList;
    private int id;

    public SongListIterator(SongList sl) {
        this.songList = sl;
        this.id = 0;
    }

    @Override
    public boolean hasNext() {
        if(id < songList.getLength()) {
            return true;
        }else {
            return false;
        }
    }

    @Override
    public Object next() {
        Song song = songList.getSongAt(id);
        id++;
        return song;
    }

}

以上で、リストを走査する準備は完了です。ここから
Mainクラスを作成して実際のリストを作り、スキャン
してみます。

Main.java
public class Main {
    public static void main(String[] args) {
        //新しくSongListを作成
        SongList songList = new SongList(5);

        //リストに曲を追加
        songList.appendSong(new Song("So What"));
        songList.appendSong(new Song("Freddie Freeloader"));
        songList.appendSong(new Song("Blue In Green"));
        songList.appendSong(new Song("All Blues"));
        songList.appendSong(new Song("Flamenco Sketches"));

        //SongIteratorのインスタンスを生成
        SongIterator song_it = songList.songIterator();

        //曲をひとつずつ調べる
        while(song_it.hasNext()) {
            Song song = (Song)song_it.next();
            System.out.println(song.getSongName());
        }
    }

}

実行結果は以下のようになりました。

So What
Freddie Freeloader
Blue In Green
All Blues
Flamenco Sketches

ちゃんと実行されたようです。やれやれ。

まとめ

つまるところ、Iteratorパターンのポイントは、
SongListを書き直しても、songIteratorメソッドが
存在し、正しいSongIteratorを返してくれれば、Mainメ
ソッドのwhileループは全く変更せずとも動作する

点にあります。要するに、機能の拡張に耐えうる設計に
なっているってことです。まさに「現場の知恵」だなと
いう風に感じます。

今後はこんな調子でGoFのデザインパターン23個、ぼち
ぼちまとめていければと。

では今日はこの辺で。ふぎとでした。

P.S. 「先輩」の皆さま、この記事に対する意見、
補足などあれば遠慮なくコメントくださいm(__)m