【デザインパターン】イテレータパターン
どうも、明日合宿だというのに木曜日に風邪引いて色々とタイミング悪いなと思ってます。
明日の合宿どうなるのか楽しみ半分、不安半分といった所でしょうか。
さて、今日は【Iterator】パターンについてです。
Iteratorパターンって何ですか?
コレクション(配列、ArrayList、Hash、Map、etc)による反復方法の違いを吸収し、同様にイテレーションが行える。
Wikipediaさんも参照。
コンテナオブジェクトの要素を列挙する手段を独立させることによって、コンテナの内部仕様に依存しない反復子を提供することを目的とする。
別にIteratorという項目もあり、そちらも掲載しておきます。
イテレータ - Wikipedia
イテレータ(英語: Iterator)とは、プログラミング言語において配列やそれに類似するデータ構造の各要素に対する繰返し処理の抽象化である。実際のプログラミング言語では、オブジェクトまたは文法などとして現れる。反復するためのものの意味で反復子(はんぷくし)と訳される。繰返子(くりかえし)という抄訳もある。
具体的な例
教科書の例では、配列とArrayListによる実装の違いを吸収するために用います。
オブジェクト町食堂とオブジェクト町パンケーキハウスが合併する事になりました。
しかし、食堂では「配列」、パンケーキハウスでは「ArrayList」を使用してメニューを管理していました。
この合併で問題になるのは、配列では`length`、ArrayListでは`size()`を使って全てのメニューを調べる必要があります。
ここでは2つの店が合併した後、ウェイトレスがメニューを全て表示する `printMenue()` 関数を作成することにします。
現在のままでは`printMenue()`の関数を作成するとどうなるか見てみましょう。
void printMenue() { // 朝食はパンケーキ店が担当 PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu(); ArrayList breakfastItems = pancakeHouseMenu.getMenuItems(); // 昼食は食堂が担当 DinerMenu dinerMenu = new DinerMenu(); MenuItem[] lunchItems = dinerMenu.getMenuItems(); for(int i = 0; i < breakfastItems.size(); ++i) { // 朝食のメニュー情報の表示 } for(int i = 0; i < lunchItems.length; ++i) { // ランチのメニュー情報の表示 } }
さて、ここで現在の処理はまだ2つだけで済みますが、他の店とも合併する場合はどんどんループが増えていくのが予想できます。
ここでIteratorパターンです。
Iteratorパターンを使用することで反復処理を統一して行うことが出来るようになります。
public interface Iterator { boolean hasNext(); Object next(); } public class DinerMenuIterator implements Iterator { MenuItem[] items; int public DinerMenuIterator(MenuItem[] items) { this.items = items; } // 次の要素へ移動する public Object next() { MenuItem menuItem = items[position]; position = position + 1; return menuItem; } // 次の要素が存在するか public boolean hasNext() { if(position >= items.length || items[position] == null) { return false; } else { return true; } } } public class PancakeIterator implements Iterator { // DinerIteratorと同様に実装を行う }
さて、これでIteratorの準備が整いました。
どのように最初のコードが改善されるかを覗いてみましょう。
void printMenu() { PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu(); DinerMenu dinerMenu = new DinerMenu(); // getMenuItemsの代わりにcreateIterator()という関数でコレクションでなくIteratorを返すように変更 Iterator pancakeIterator = pancakeHouseMenu.createIterator(); Iterator dinerIterator = dinerMenu.createIterator(); printMenu(pancakeIterator); printMenu(dinerIterator); } void printMenu(Iterator iterator) { while(iterator.hasNext()) { // メニューを表示する処理 } }
どうでしょうか?
先ほど、2つに分かれていた処理が1つに統合されました。
PancakeHouseMenuとDinerMenuではgetMenuItems()という関数でなくcreateIterator()という処理に変えただけで、他には手を加えていません*1。
これにより、どれだけ異なるコレクションを使ってもループが別れることがありません。
これがIteratorパターンです。