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なのでちょっとあれだけど知らん。

    /** A unified logging interface */
    interface Logger{
    log(msg: string): void;
    }
    /** Factory for enums */
    class LOGGER_TYPE{
    static CONSOLE:string="CONSOLE";
    static ALERT:string="ALERT";
    static DISPLAY:string="DISPLAY";
    }
    class LogAdapterFactory{
    static createLogger(type:string): Logger{
    switch(type){
    case LOGGER_TYPE.CONSOLE:
    return new ConsoleLogger();
    case LOGGER_TYPE.ALERT:
    return new AlertLogger();
    case LOGGER_TYPE.DISPLAY:
    return new DisplayLogger();
    }
    return null;
    }
    }
    class ConsoleLogger implements Logger{
    log(msg:string){
    console.log(msg);
    }
    }
    class AlertLogger implements Logger{
    log(msg:string){
    alert(msg);
    }
    }
    class DisplayLogger implements Logger{
    display:HTMLDivElement;
    count:number;
    constructor(){
    this.count = 0;
    this.display = <HTMLDivElement>document.createElement("div");
    this.display.style.position="absolute";
    this.display.style.top="0px";
    this.display.style.right="0px";
    this.display.style.width="200px";
    this.display.style.background="#ddffdd";
    this.display.style.padding="10px";
    this.display.style.fontSize="10px";
    document.body.appendChild(this.display);
    }
    log(msg:string){
    if(this.count > 10){
    this.display.removeChild(this.display.childNodes[0]);
    }
    else{
    this.count++;
    }
    this.display.innerHTML += "<div>"+msg+"</div>";
    }
    }
    view raw gistfile1.js hosted with ❤ by GitHub


    使う


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

    <html>
    <head>
    <script type="text/javascript" src="dp.js"></script>
    <script type="text/javascript">
    var logger;
    window.onload = function(){
    logger = LogAdapterFactory.createLogger(LOGGER_TYPE.DISPLAY);
    logger.log("output log");
    }
    </script>
    </head>
    <body>
    <input type="text" value="write log here." id="log" />
    <input type="button" value="出力" onclick="logger.log(document.getElementById('log').value)" />
    </body>
    </html>
    view raw index.html hosted with ❤ by GitHub


    終わりに


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

    Typescriptええで

    0 件のコメント:

    コメントを投稿