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月8日火曜日

2012年振り返り&2013年の目標

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
2012年も色々あったなぁーと。かいつまんで自分用に整頓。2013年の目標も。

2012年振り返り


はじめて確定申告した


大変だつた。
別に儲かってないです。

デブサミ2012出た


登壇させて頂いた。恐縮だす。大変だつた。
スライド
http://www.slideshare.net/hyoromo/201216d7ios-android-windows-phone
http://www.slideshare.net/hyoromo/201216d7ios-android-windows-phone-11631786


本書いた


まさかAmazonに自分の名前が載るとは本当に人生ってよくわかりません・・・。皆様のおかげ。大変だつた。




ソーシャルゲームの開発した


PHP,Ruby,Capistrano,Python,java,Smarty,MySQLとか使った。
独自Webフレームワーク作った。そんなに良い設計では無かった。大変だつた。


Google Playの某カテゴリで「青空文庫ビューア」が有料人気4位になった



Google Playの某カテゴリで「青空文庫ビューア」が有料人気4位になったわけだが収益を赤裸々に綴ってみる

このザマ。大変だつた。
青空文庫ビューア Ad使ってね!


AOSPにパッチ投げた


aaptを修正してres配下でディレクトリ階層を構築できるようにして、AOSPにrepo uploadした話
が、だめ。諦めない。大変だつた。


社内の会議でLTやるようにした


発表資料を作る事と、発表する事の練習の為に。月一の会議の際に。
社内なのでスライドは出せない(出してもいい気もするけど)が、タイトルだけ

  • ゆるふわHaskell入門
  • 技術者なら読んでるはずの本おさらい2012秋
  • 30歳までにやっておいてよかった事 30歳までにやっておけばよかった事
  • AOSPにパッチ投げてみた

  • 月一回とは言え業務を縫ってなので大変だつた。


    社内のネイティブの人に開催してもらっている英語の勉強ミーティングに参加


    Hello everyone!
    これは昼休み。まだ全然話せない。でも面白い。でも大変だつた。


    新人研修をやった


    テーマがあったのでそれにそってカリキュラムを考えたりなんやしたり。
    まぁ色々アレで色々大変だつた。


    Sublime Text 2のプラグインを作った


    Sublime Text 2のステータスバー上で素数を数えるだけの画期的なプラグインを作りました。
    とか
    Bing Translator APIを使ったSublime Text 2の翻訳プラグインが無かったので作った話

    pythonは兎も角package_control_channelに登録する為のpull requestとかが大変だつた。


    Haskellに入門した


    すごいH本で入門。まだまだだけどおもろい。モナドが大変だつた。
    あとWebフレームワークのYesodがCentOSで構築できなくて大変だつた。結局Ubuntu Serverにして大変だつた。


    2013年の目標


    ざっと。今思いつく範囲で。

    仕事


    ・Jenkins氏を仕事で使う
    ・TDDやる
    ・時間管理まじ頑張る
    ・手帳を持つ


    技術


    ・AOSPに貢献する
    ・本を書く
    ・何らか講演する
    ・OSSライブラリを作る
    ・Haskellもっとやる
    ・Node.jsやる
    ・Eclipseプラグイン作る
    ・OS作る
    ・Typescriptやる


    その他


    ・音楽を作る 12曲!
    ・TOEICで600とる
    ・身体を鍛える
    ・ジョギングする


    がんばる


    がんばる


    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ええで

    2013年1月4日金曜日

    Androidでjava1.7をどうしても使いたい場合はこうしたらええで

    このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
    Androidでjava1.7は使えないらしい、けど、何とかする方法はありました。
    結論からいうと別においしくない。

    何故Androidでjava1.7が使えないか


    javaをコンパイルして生成されるclassファイルの中には、「オレ、このバージョンでコンパイルされてるんでヨロ」という情報が入ってます。んでjavaの実行環境側でclassファイルをロードする時にサポートしているバージョンでコンパイルされたclassファイルかどうかをチェックして、もしサポート外ならエラーを投げる様になってます。

    Androidだとclassを生成後、dexファイルに変換する所で以下の様に怒られます。
    bad class file magic (cafebabe) or version (0033.0000)
    
    「このきたならしい阿呆がァーッ!!」
    classファイルのversionが間違ってると言われてますね。
    0x33は51です。java1.7でコンパイルするとclassファイルのバージョンは51になるんすねー。1.6なら50。

    で、どうする


    dexで怒られるのでAndroidで使えないのは分かりました。じゃあどうしたらええのか。長いjavaの歴史、互換性に関しても色々ノウハウあるんちゃうん、という事でありました。下位互換のオプションが。

    Java7シンタックスで レガシーコードを快適メンテナンス
    とか
    javac - Java プログラミング言語コンパイラ
    を見ますと、javacに-target付けてコンパチなclassファイルが作れるようですね

    Eclipse+ADTでは無理


    結論から言うとEclipseでは無理です。
    設定で以下の様に生成するclassファイルのバージョン指定とか出来ますが、



    ADTに怒られます。

    Android requires compiler compliance level 5.0 or 6.0. Found '1.7' instead. Please use Android Tools > Fix Project Properties.

    無理です。

    antならいける


    antではどうでしょうか。

    Android用のbuild.xmlを見る


    とりあえずANDROID_HOME/tools/ant/build.xmlの-compileタスクを見てみます。このbuild.xmlはandroidコマンドなどで生成したAndroidプロジェクトに生成されるbuild.xmlでimportされているAndroidアプリケーションをビルドする為のタスクが詰まっているファイルです。
        <target name="-compile" depends="-build-setup, -pre-build, -code-gen, -pre-compile">
            <do-only-if-manifest-hasCode elseText="hasCode = false. Skipping...">
                <!-- merge the project's own classpath and the tested project's classpath -->
                <path id="project.javac.classpath">
                    <path refid="project.all.jars.path" />
                    <path refid="tested.project.classpath" />
                </path>
                <javac encoding="${java.encoding}"
                        source="${java.source}" target="${java.target}"
                        debug="true" extdirs="" includeantruntime="false"
                        destdir="${out.classes.absolute.dir}"
                        bootclasspathref="project.target.class.path"
                        verbose="${verbose}"
                        classpathref="project.javac.classpath"
                        fork="${need.javac.fork}">
                    <src path="${source.absolute.dir}" />
                    <src path="${gen.absolute.dir}" />
                    <compilerarg line="${java.compilerargs}" />
                </javac>
    ~~略~~
    

    javacタスクの所にtargetというのがありますね。
    値は${java.target}という変数です。
    ${java.target}の定義を見ると
        <property name="java.target" value="1.5" />
        <property name="java.source" value="1.5" />
    

    1.5になってますねー。
    java.sourceの方はjavaファイルがどのバージョンかを表すオプション-sourceに渡す値です。こちらも1.5になっています。

    これらを以下の様にするとどうでしょうか
        <property name="java.target" value="jsr14" />
        <property name="java.source" value="1.7" />
    

    antでビルドしてテスト走らしてみる


    上の設定にした状態で、以下のテストコードをBuild&Runしてみます。
    ちなみにJUnit4 works on Androidあたりを参考にしてjunit4に対応させています。
        @Test
        public void Itemテーブルにレコードを1件追加する(){
            String moge = "ふがふが";
            switch(moge){
            case "ふがふが":
                Log.d("tag", "hogeeeeee!");
                break;
            case "moge":
                Log.d("tag", "moge");
                break;
            default:
                Log.d("tag", "あれえ");
            }
        }
    

    はいビルド&実行
    ant debug installd
    ant test
    

    キタ━━━━(゚∀゚)━━━━!!
    I/System.out(13216): org.junit.runners.Suite
    I/TestRunner(13216): started: Itemテーブルにレコードを1件追加する(jp.dip.sys1.yagi.amp.sample.dao.ItemDaoBaseTest)
    D/tag     (13216): hogeeeeee!
    I/TestRunner(13216): finished: Itemテーブルにレコードを1件追加する(jp.dip.sys1.yagi.amp.sample.dao.ItemDaoBaseTest)
    

    いけましたね。

    ANDROID_HOME/tools/ant/build.xmlをいじるのは嫌だよね


    基本的にANDROID_HOME配下のヤツをいじって何かに対応するのは緊急の場合を除いてしたくないっすね。もろに環境依存だし。つーことで、なんとかANDROID_HOME/tools/ant/build.xmlのプロパティを外部からいじれねーかと、もちろんいじれます。
    ant debug -Djava.target=jsr14 -Djava.source=1.7
    

    こうです。-Dオプションです。debugの所はreleaseとかinstrumentでももちいけますです。これでjava.targetとかが書き換えられます。

    終わりに


    java1.7の機能でめぼしいのってtry-with-resourcesと例外のマルチキャッチくらいだなーと思ってるけどjsr14では

    try-with-resourcesは使えないようなので

    なんだかなーと。(try-with-resourcesはAutoCloseableというjava1.7で追加されたinterfaceで実現しているので当然1.6以前には存在しないから無理ぽという事。これ以外でもjava1.7で追加されたAPIとかはもち使えないと思う。)
    あとEclipseで使えないので実用性はあんまりないなーと。
    どーしても使いたい、という場合向けだけど微妙。

    以上