Going my way

いいなと思ったことをメモしていきます。

恋するデザインパターン(Iterator編)


Advertisements


デザインパターンIteratorパターンを、彼女がたくさんいるプレイボーイに例えて説明します。
明日のプレイボーイオブジェクトになるために頑張りましょう。

Iteratorとは、何かを1つ1つ数え上げるための機能です。
例えば、あなたがプレイボーイだったとしましょう。

あなたにはたくさんの彼女がいます。

その何人もいる彼女を「彼女リスト」という手帳に書いて、一人ひとり指をさしながら数えていくとします。

この数えるための「指」の働きをするのがIteratorです。

つまり、Iteratorとは、何かの集合体を数え上げるために、「その対象を指し示すもの」の役割を抽象化したものです。

では、Iteratorパターンを実際のコードで見てみましょう。

登場人物は以下の通りです。

・Aggregateさん
→「集合体」を抽象化したインターフェースです。集合体を作る時はこのインターフェースを実装します。

・KanojyoListさん
→手帳に書いた「彼女のリスト」になります。「彼女の集合体」を表すため、Aggregateさんをimplementsします。

・Kanojyoさん
→彼女の要素です。この彼女の振る舞いは「自分の名前を返すだけ」です。本当はスリーサイズをセットしたいのですが・・

Iteratorさん
→「集合体を指し示す」ための機能を抽象化したものです。集合の何かを指差したい機能を作る時は、このインターフェースをimplementsします。

・KanojoListIteratorさん
→彼女リストにある彼女を順に指し示す機能を実装したものです。指し示す機能を作っているので、Iteratorをimplementsします。

・Mainさん
→ここからプログラムが始まります。彼女を持てる限界人数を設定して、彼女リストにどんどん彼女を追加していきます。あなたそのものと考えてもいいでしょう。


それでは、コードを見ていきましょう。

・Aggregateさん

package iterator;

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

ここでは集合体に必須の機能としてIteratorをabstractで定義しておくことで、
どんな集合体のクラスを作ってもAggregateさせimplementsしていれば、必ずiterator機能があることが保証されます。


・KanojyoListさん

package iterator;

public class KanojyoList implements Aggregate {
	private Kanojyo[] kanojyoTachi;
	private int last = 0;
	
	public KanojyoList(int genkaiNinzu) {
		this.kanojyoTachi = new Kanojyo[genkaiNinzu];
	}
	
	public Kanojyo getKanojyoNumber(int index) {
		return kanojyoTachi[index];
	}
	public void addKanojyo(Kanojyo kanojyo) {
		if(last < kanojyoTachi.length){
			this.kanojyoTachi[last] = kanojyo;
			last++;
		}else{
			System.err.println("これ以上彼女を捌ききれません!!");
			System.err.println(kanojyo.getName() + "とは断腸の想いで別れます");
		}
	}
	public int getKanojyoNinzu(){
		return last;
	}
	public Iterator iterator(){
		return new KanojyoListIterator(this);
	}
}

・Kanojyoさん

package iterator;

public class Kanojyo {
	private String name;
	public Kanojyo(String name){
		this.name = name;
	}
	public String getName() {
		return name;
	}
}

Iteratorさん

package iterator;

public interface Iterator {
	public abstract boolean hasNext();
	public abstract Object next();
}

・KanojyoListIteratorさん

package iterator;

public class KanojyoListIterator implements Iterator {
	private KanojyoList kanojyoList;
	private int index;
	public KanojyoListIterator(KanojyoList kanojyoList){
		this.kanojyoList = kanojyoList;
		this.index = 0;
	}
	
	public boolean hasNext(){
		if(index < kanojyoList.getKanojyoNinzu()){
			return true;
		} else {
			return false;
		}
	}
	public Object next(){
		Kanojyo kanojyo = kanojyoList.getKanojyoNumber(index);
		index++;
		return kanojyo;
	}
	
}

・Mainさん

package iterator;

public class Main {
	private static int genkaiNinzu = 5;
	public static void main(String[] args){
		KanojyoList kanojyoList = new KanojyoList(genkaiNinzu);
		kanojyoList.addKanojyo(new Kanojyo("沙織"));
		kanojyoList.addKanojyo(new Kanojyo("さやか"));
		kanojyoList.addKanojyo(new Kanojyo("志穂"));
		kanojyoList.addKanojyo(new Kanojyo("由利"));
		kanojyoList.addKanojyo(new Kanojyo("明代"));
		kanojyoList.addKanojyo(new Kanojyo("綾"));
		
		Iterator ite = kanojyoList.iterator();
		while (ite.hasNext()) {
			Kanojyo kanojyo = (Kanojyo)ite.next();
			System.out.println(kanojyo.getName());
		}
		
	}
}

実行結果は以下の通り。

これ以上彼女を捌ききれません!!
綾とは断腸の想いで別れます
沙織
さやか
志穂
由利
明代

mainで定義した限界人数を超えてリストに追加しようとしたときは、エラーを吐きます

その下でiteratorで一人ひとりの彼女の名前をなめるように吐き出しています。


このように一つ一つの機能を抽象化して分けることで、プログラム同士が疎結合になります。
あるクラスの変更が、他のクラスに影響を与えづらくなるということです。

また、Interfaceに機能を定義しておけば、それを使う側はinterfaceの機能だけ知っておけば、
機能の中身を見なくても必ずinterfaceにあるメソッドは使うことができると保証されています。

大きなプログラムになるとどこのクラスでどのメソッドを使っているかわからなくなってくるため、
このようにクラスに共通する機能をinterfaceとして抽象化することで、仕様と実装を分離することができます。

使う側は仕様を知っていれば、実装を知らなくても機能を使うことができるのです。