Iteratorパターン実装してみた

どーも、ふぎとです。

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


##Iteratorパターンとは


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

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

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

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

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

~~~ruby:SongAggregate.java
public interface SongAggregate {
	public abstract SongIterator songIterator();
}
~~~
このインターフェースでは、集合体を数えあげる
ためのメソッド「songIterator」のみを宣言します。

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

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

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

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

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

~~~ruby:Song.java
public class Song {
	private String songName;

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

	public String getSongName() {
		return songName;
	}
}
~~~

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

~~~ruby: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メソッドをオーバー
ライドします。

~~~ruby: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クラスを作成して実際のリストを作り、スキャン
してみます。


~~~ruby: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