ラベル デザインパターン の投稿を表示しています。 すべての投稿を表示
ラベル デザインパターン の投稿を表示しています。 すべての投稿を表示

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を実装してみたいと思います。

2013年1月10日木曜日

第二回 Typescriptでデザインパターン: Bridge Pattern

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

Bridge Patternとは


Bridge Patternは機能と実装を分け、それらを「橋渡し」する事で設計の柔軟性を確保する為のパターンです。
Bridge パターン Wikipedia

Bridge Patternを実装するには


TypescriptでのBridge Patternの実装はinterfaceと抽象クラスが必要となります。Typescriptでは抽象クラスがありません。今回は以下の様な実装にして擬似的に抽象クラス的なものを実現しています。
class Pattern{
  //略
  draw(canvas:HTMLCanvasElement){
    throw new Error("not yet implemented.");
  }
}

Patternをインスタンス化してdrawを呼ぶと死にます。この実装の場合実行時にエラーを吐く事になるので、コンパイル時に型チェックなどをするTypescriptの恩恵を受けられません。

Bridge Patternの実装


何を作るか


図形を描画するShapeと、Shapeをルールにしたがって動かすPatternを作ってみます。
今回Shapeは四角形と円にしました。
Patternは左右移動とランダム移動を作りました。

ソースはこちら
https://gist.github.com/4494073

動作は以下。


Bridgeパターンの何がおいしいか


この図形描画の機能に、図形を追加したい場合どうすればよいでしょうか?簡単ですね。Shapeをimplementsしたクラスを作れば良いです。Pattern側には何も影響を与えません。Pattern側も同様に、動きのバリエーションを増やす時、Shapeの事を考えなくて済みます。「橋渡し」の仕組みによって互いに疎である事を実現していますね。

終わりに


今回もTypescriptのクラス、インタフェースの仕組みによって自然と設計、実装が出来ました。可読性の面でも、jsにコンパイルされた方はprototypeとか__extendsとか色々余計なキーワードが出てきてややこしく感じます。Typescriptの方はclassの定義や継承関係などもはっきり書かれていて簡潔なため読みやすいです。

また、仕様を変更した時にコンパイルエラーによって事前にエラーを何度か発見する事が出来ました。昔はalertやconsole.logやらChomeのDeveloper Toolsなどで実行しながら挙動を追う事をしていました。Typescriptならコンパイルの際に色々と検査してくれるので凄く助かります。

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

2013年1月6日日曜日

第一回 Typescriptでデザインパターン: Adapter Pattern

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク


Typescriptでデザインパターンをやる。
どうせなのでGoF網羅しよう。interpreter patternはちょっと無理かもしれませんが。

Adapter Patternとは?


Adapter パターン
Adapter パターンを用いると、既存のクラスに対して修正を加えることなく、インタフェースを変更することができる。Adapter パターンを実現するための手法として継承を利用した手法と委譲を利用した手法が存在する。

だそうです。

Adapter Patternを実装するには?


Adapter Patternはinterfaceがあればやれるはず、Typescriptにinterfaceはあるか?

ある。
interface Logger{
 log(msg: string): void;
}

こういう感じで書ける。interfaceはtscでコンパイルする時にだけ使われる。
interfaceをimplementsしているclassがintefaceを満たすかチェックしたり、interfaceを取り扱っている部分での型チェックなどを行う。コンパイル後のjsにはinterfaceは出力されない。javascriptにはinterfaceに準ずる機能はないので当然かもしれない。

このintefaceを使えばAdapter Patternを実装する事が出来そうだ。

実装


javascriptでログ出力を行う方法はパッと以下の3つが考えられる。
  • アラートで出す
  • コンソールに出す
  • ログ用要素に出す


  • それぞれで出力の仕方が異なるのでAdapter Patternを使ってログ出力のインタフェースを統一する。

    実装は以下。intefaceとしてLoggerを定義し、それぞれの出力方法のAdapterを作る。ぞれぞれのLoggerはLogAdapterFactoryを通して受け取る。LogAdapterFactoryにはLOGGER_TYPEを渡す。Typescriptにenumはないので擬似でLOGGER_TYPEというのを作った。内部的には唯のstringなのでちょっとあれだけど知らん。



    使う


    Loggerを使ってみる。LogAdapterFactory.createLogger(LOGGER_TYPE.DISPLAY);のLOGGER_TYPEを変えればそれぞれの出力に切り替わる。



    終わりに


    javascriptでこれを実現するコードを書くのは別に難しくない、けど、今までそういう事をしようとした場合「js的にこうでいいかなぁ?」と毎回悩んでいた。Typescriptだとこの辺りをすっ飛ばして設計に専念しやすいと感じる。

    Typescriptええで

    2011年12月30日金曜日

    Chain Of ResponsibilityパターンによるFizzBuzz実装

    このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
    出落ち感満載ですが、実装してみました。
    単純にGoFデザインパターンの一つであるChain Of Responsibilityパターンを使ってみたかっただけです。
    今まで実際に使った事が無かったので・・・。

    Chain Of Responsibilityパターンって?


    ggrks。
    Wikipedia:Chain of Responsibility パターン
    簡単に言うと責任の鎖。「ちょっと責任者に代わって。君じゃ埒があかん。」とこういう事ですね。


    実装


    ChainOfResponsibilityクラスのインナークラスとして全て書いています。
    Fizz, Buzz, FizzBuzz, Integersクラスは抽象クラスであるHandlerを継承しています。
    ChainOfResponsibility#fizzBuzz()の中でsetNextしてお互いを連結しています。

    Handler#fizzBuzz(int)で実行。inCharge(int)で自分がこの値を担当しているかどうかをbooleanで返します。担当であればpring(int)を実行して終了。担当じゃなかったら自分の次の人のHandler#fizzBuzz(int)を呼び出します。

    例えば65という値が入れられた場合はFizzBuzz->Fizz->Buzzまで行ってprintされます。

    なんとなくFizzBuzzクラスやIntegersクラスのinCharge(int)はFizzクラスとBuzzクラスを使って判定させてます。
    連結の仕方によって計算量が変わる気がしますがなんだか面倒なので考えるのをやめました。
    もっと短く出来ると思いますが面倒なので(ry

    public class ChainOfResponsibility {
        static public abstract class Handler{
            private Handler next = null;
           
            public Handler setNext(Handler next){
                this.next = next;
                return next;
            }
            public final void fizzBuzz(int value){
                if(inCharge(value)){
                    print(value);
                }else if(next != null){
                    next.fizzBuzz(value);
                }else{
                    throw new RuntimeException("Oops!");
                }
            }
            protected abstract boolean inCharge(int value);
            protected abstract void print(int value);
        }
        
        static class Integers extends Handler{
            Fizz fizz = new Fizz();
            Buzz buzz = new Buzz();
            @Override
            protected boolean inCharge(int value) {
                return !fizz.inCharge(value) && !buzz.inCharge(value);
            }
            @Override
            protected void print(int value) {
                System.out.println(value);
            }
        }
        static class Fizz extends Handler{
            @Override
            protected boolean inCharge(int value) {
                return value%3 == 0;
            }
            @Override
            protected void print(int value) {
                System.out.println("Fizz");
            }
        }
        static class Buzz extends Handler{
            @Override
            protected boolean inCharge(int value) {
                return value%5 == 0;
            }
            @Override
            protected void print(int value) {
                System.out.println("Buzz");
            }
        }
        
        static class FizzBuzz extends Handler{
            Fizz fizz = new Fizz();
            Buzz buzz = new Buzz();
            @Override
            protected boolean inCharge(int value) {
                return fizz.inCharge(value) && buzz.inCharge(value); 
            }
            @Override
            protected void print(int value) {
                System.out.println("FizzBuzz");
            }
        }
        
        public void fizzBuzz(){
            Handler integer = new Integers();
            Handler fizz = new Fizz();
            Handler buzz = new Buzz();
            Handler fizzBuzz = new FizzBuzz();
            
            fizzBuzz.setNext(fizz).setNext(buzz).setNext(integer);
            for(int i = 1; i <= 100; i++){
                fizzBuzz.fizzBuzz(i);
            }
        }
        
        public static void main(String[] args) {
            new ChainOfResponsibility().fizzBuzz();
        }
    }
    

    まとめ


    このパターンはいろいろ使えそうだなぁ。
    デザインパターンによるFizzBuzz実装をシリーズ化できるかなー。どうかな。