【デザインパターン】コンポジットパターン
普通に書くの忘れてました。
(´ぅω・`)ネムイ
さて、今日は【Composite】パターンについてです。
Compositeパターンって何ですか?
前回触れた (【デザインパターン】イテレータパターン - datchの日記) にも関係しており、
構造的なデータを再帰的に表すためのデザインパターンです。
Wikipediaさんも参照。
Composite パターンを用いるとディレクトリとファイルなどのような、木構造を伴う再帰的なデータ構造を表すことができる。
具体的な例
では、いつも通り参考書の例を出してみましょう。
前回のIteratorパターンにより、ArrayListやArrayなどの異なるコンテナにおいても同様にイテレーションを行う事が出来る様になり、ウェイトレスに聞けば店のメニューはすぐに分かるようになったぞ!
さて、ここでウェイトレスにパンケーキ店のメニュー、食堂のメニュー、カフェのメニューを渡すことでそれぞれのメニューを表示している。
さて、実際にソースコードを覗いてみよう。
public void printMenue(){ Iterator pancakeIterator = pancakeHouseMenu.createIterator(); Iterator dinerIterator = dinerMenu.createIterator(); Iterator cafeIterator = cafeMenu.createIterator(); printMenue(pancakeIterator); printMenue(dinerIterator); printMenue(cafeIterator); }
これではメニューリストが増えてきては大変です!
そこで、それぞれのIteratorをArrayListに格納して対処するようにしました。
public class Waitress{ ArrayList menus; public Waitress(ArrayList menus){ this.menus = menus; } public void printMenue(){ Iterator menuIterator = menus.iterator(); while(menuIterator.hasNext()){ Menu menu = (Menu)menuIterator.next(); printMenu(menu.createIterator()); } } public void printMenu(Iterator iterator){ // あとは同様にイテレーション } }
さて、これで万事解決です!
と、思った矢先に以下の様な要件が追加されました。
「メニュー内にデザートのサブメニューを追加したい!」
これは困りました。
今まではメニューの品物だけで、サブメニューが入る予定がなかったからです。
さて、ここで威力を発揮するのが今回の「Compositeパターン」になります。
現在のデータ構造の状態は以下のようになっています。
- 引用:
先ほどまでは品物のみで、図で言えば「葉」に相当する物のみでした。
しかし、ここに来てサブメニューという「枝」が追加されたので、Compositeパターンを用います。
Compositeは以下の木構造を構築するために、以下のような命令を有します。
- add : 子の追加
- remove : 子の削除
- getChild : 子の取得
これを参考にして更にメニュー表示に必要な以下の関数も追加します。
- getname
- getDescription
- getPrice
- isVesitable
さて、ではこの関数を保持するCompositeクラスを作成していきましょう。
public abstract class MenuComponent{ // コンポジットパターンの処理 public void add(MenuComponent menuComponent){ throw new UnsupportedOperationException(); } public void remove(MenuComponent menuComponent){ throw new UnsupportedOperationException(); } public MenuComponent getChild(int i){ throw new UnsupportedOperationException(); } // 独自拡張の処理 public String getName(){ throw new UnsupportedOperationException(); } public String getDescription(){ throw new UnsupportedOperationException(); } public String getPrice(){ throw new UnsupportedOperationException(); } public String isVegitable(){ throw new UnsupportedOperationException(); } public void print(){ throw new UnsupportedOperationException(); } }
さて、それではこのCompositeクラスを拡張していきましょう。
まずは「葉」に相当する部分を作っていきます。
public class MenuItem extends MenuComponent{ String name; String description; boolean vegetarian; double price; public MenuItem(String name, String description, boolean vegetarian, double price){ this.name = name; this.description = description; this.vegetarian = vegetarian; this.price = price; } // 独自拡張の処理は省略 public void print(){ // 品物情報の表示 } }
次は「枝」に相当する部分です。
Compositeパターンでこのようなクラスを「コンポジット」と呼びます。
public class Menu extends MenuComponent{ ArrayList menuComponents = new ArrayList(); String name; String description; public Menu(String name, String description){ this.name = name; this.description = description; } // コンポジットパターンの処理 public void add(MenuComponent menuComponent){ menuComponents.add(menuComponent); } public void remove(MenuComponent menuComponent){ menuComponents.remove(menuComponent); } public MenuComponent getChild(int i){ retur (MenuComponent)menuComponents.get(i); } // 独自拡張の処理は省略 public void print(){ // メニュー情報の表示 // !!重要!! Iterator iterator = menuComponents.iterator(); while(iterator.hasNext()){ MenuComponet menuComponent = (MenuComponent)iterator.next(); menuComponent.print(); } } }
これがCompositeパターンです!
Menu部分のprint()では、それがMenuなのかMenuItemなのかという型の違いを吸収し、全てのメニュー一覧を表示することが出来るようになりました!
再帰や幅優先探索に親しみのある人は理解がし易いと思います。
これで複雑なデータ構造を持つ情報も、シンプルに取り扱うことが出来るようになりました。
最後に
非常に眠いです。