2013年1月13日日曜日

第三回 Typescriptでデザインパターン: Composite Pattern

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
第三回はComposite Patternを実装してみます。

Composite Patternとは


Composite Patternは木構造を伴う再帰的なデータ構造を表すことができます。同じインタフェースを持つ枝と葉が、再帰的な木構造を実現するんですねー。ファイルとディレクトリの関係がまさにComposite Patternです。
Composite パターン Wikipedia

Composite Patternを実装するには


TypescriptでのComposite Patternの実装はinterfaceがあれば実現出来ます。登場人物はComponentとLeafとCompositeです。各要素をクラス図にしてみました。LeafとCompositeはComponentインタフェースを実現しています。CompositeはComponentを集約しています。



Component


LeafとCompositeで共通となるインタフェースです。
木構造を実現する為の機能と、Leaf、Compositeが提供する機能を定義します。

Leaf


木の末端である葉を表すクラスです。

Composite


木の枝を表すクラスです。内部に葉か枝を複数持ちます。Componentの内、主に子要素を操作する機能を実現します。


Composite Patternの実装


何を作るか


ファイルとディレクトリを模した構造を作ってみます。共通のインタフェースであるComponentとファイルを表すItemクラス、ディレクトリを表すDirectoryクラスで構成されています。木構造のrootにDirectoryクラスを配置し、addメソッドでItemかDirectoryを追加して利用します。

Componentが子要素を持つDirectoryクラスか、葉であるItemかを判定する為のisItemメソッドを用意しました。このメソッドによってDirectory,Itemを判定し、再帰処理に利用します。

Componentインタフェースには子要素を操作するメソッドがいくつかあります。Itemクラスではそれらを利用しないので、呼び出した場合は例外を送出する様にしました。例外以外にも無効な値を返却するという方法も考えられます。

実装


//Composite Pattern

//component
interface Component{
  add(child:Component):bool;
  remove(child:Component):bool;
  getChildren():Component[];
  getName():string;
  isItem():bool;
}
//leaf
class Item implements Component {
  constructor(private name: string){

  }
  add(child:Component):bool{
    throw new Error("this object is Item.");
  }
  remove(child:Component):bool{
    throw new Error("this object is Item.");
  }
  getChildren():Component[]{
    throw new Error("this object is Item.");
  }
  getName():string{
    return this.name;
  }
  isItem():bool{
    return true;
  }
}
//composite
class Directory implements Component{
  items:Component[];
  constructor(private name: string){
    this.items = new Array();
  }
  add(child:Component):bool{
    this.items.push(child);
    return true;
  }
  remove(child:Component):bool{
    for (var i : number = this.items.length - 1; i >= 0; i--) {
      if(this.items[i].getName() === child.getName()){
        this.items.splice(i, 1);
        i++;
      }
    }
    return true;
  }
  getChildren():Component[]{
    return this.items;
  }
  getName():string{
    return this.name;
  }
  isItem():bool{
    return false;
  }
}

ソースはこちら
https://github.com/sys1yagi/gof_design_pattern_implemented_in_Typescript/tree/decorator_pattern/src/03.Composite%20Pattern

動作


Composite Patternを用いた木構造でファイルとディレクトリを構成し、再帰的に木を走査して全要素を表示するサンプルです。子要素はpaddingを付けるようにしています。簡単に階層構造を表現出来ます。



Composite Patternの何がおいしいか


Composite PatternはComponentを再帰的に集約する点が拡張性を高めています。今回はConpositeとItemの二つの構成でしたが、ConpositeやItemの種類を増やしたりする事も容易に出来ます。DSLを作る為のInterpreter PatternもComposite Patternの一種です。

終わりに


Webではパンくずメニューや、サイドバーなどのメニューなどで使えると思います。その他にはフォームの入力チェックなんかにも使えるんじゃないかなーと思います。

次回はDecorator Patternを実装してみたいと思います。

0 件のコメント:

コメントを投稿