[TypeScript] イベント Delegateパターン

2013/08/7

こんにちは。きんくまです。

暑い日が続きますね。なんだか忙しくなってきたので、気晴らしに記事投稿です。

このブログのアクセスログを見てたら、iOSのProtocol – Delgateパターンの記事が上の方に来てました。

>> [iOS] Protocol – Delegateパターン | Objective-C イベント伝達 その1

その1とか書いておいて、その2以降を書いてません!
スミマセン!

それで、TypeScriptでも同じパターンのものを書いて使ってます。なのでその紹介です。

これは何?

簡単にいうとコールバック処理です。
AとBというオブジェクトがあって、Bに何かイベントがおきたときに、Aに処理を返したいときに使います。
もちろん生のJavaScriptでも作れます。

これから説明する、TypeScriptでInterfaceを使う場合は、情報が整理されているので、見やすくなるといった利点があるかと思われます。

今回のデモ

今回つくるデモページです。ボタンをクリックすると情報が表示されます。

>> デモページ

Interfaceを定義

イベントが起きたときの取り決めを書きます。

    export interface IBasicButtonDelegate{
        buttonClicked?(button:BasicButton, pageX:number, pageY:number):void;
        //他にいくつつくっても大丈夫
    }

今回はボタンがクリックしたイベント時に使うので、それ用です。
buttonClicked? となっていて、メソッド名のあとに?がついています。
TypeScriptでは?はオプション扱いなので、この場合はイベントを受け取る側は実装してもしなくてもよいということになります。もし必ず実装させたい場合は?なしで書いてあげます。
その後の引数3つは、適当です。渡したい情報を入れてあげるといいと思います。
この引数もオプションにしたり、しなかったりとするとよいかと。

今回はデモなので、メソッドをひとつだけ定義しましたけど、いくつ作っても大丈夫です。
あるデータが更新されたときとか、あるビューがスクロールされたときとか、いろんなイベントがあると思います。

イベント発行側

    export class BasicButton{
        delegate:IBasicButtonDelegate; //定義したinterface型
        selector:string;

        constructor(containerSelector:string){
            this.selector = containerSelector;
            this.setup();
        }

        setup():void{
            $(this.selector)
            .click((e)=>{
                //存在をチェック
                if(this.delegate && this.delegate.buttonClicked){
                    //イベント発行!
                    this.delegate.buttonClicked(this, e.pageX, e.pageY);
                }
            });

        }
    }

さきほどのinterfaceの型が入っているdelegateというプロパティを持っています。
それで、イベントを出したいときに、空かどうか判定して、実行します。

イベント受け取り側

最後にイベント受け取り側です。

    //implementsする
    export class Main implements IBasicButtonDelegate{
        mybutton:BasicButton;

        constructor(){
        }

        init():void{
            this.mybutton = new BasicButton('#button_container');
            //delegateに自分を代入
            this.mybutton.delegate = this;
        }

        //interfaceで定義したメソッドを実装
        buttonClicked(button:BasicButton, pageX:number, pageY:number):void{
            $('#logarea').append('<p>clicked pageX = ' + pageX + ' , pageY = ' + pageY + '</p>');
        }
    }

interfaceで定義したメソッドを実装してあげることで、そこにコールバックが返ってきます。

どの辺が便利?

interfaceを定義することで、イベントの発行側と受け取り側に共通の約束事ができます。
なので、間違いがおきにくいところあたりでしょうか、、。

個人的には型みれば、そいつが何をするためのものなのかがわかって、便利かなと思います。

感想とか

TypeScriptは型づけされているので、コンパイルチェックとかされてミスも少なくてすむので便利だなと思ってます。あとコードヒントの出るエディタでやると、すごく速くコードが書けて楽です。

あ、あともしこのサンプルの例で、Mainのプロパティのmybuttonを消去するときは、

this.mybutton.delegate = null;
delete this.mybutton;

とdelegateにnull代入をやっておくとよいかもしれないです。メモリリークが防げるかも。いらないかもしんない(適当)。

今回の記事の全部のソースです

main.ts

/// <reference path="jquery.d.ts" />

module kk{
    export interface IBasicButtonDelegate{
        buttonClicked?(button:BasicButton, pageX:number, pageY:number):void;
    }

    export class BasicButton{
        delegate:IBasicButtonDelegate;
        selector:string;

        constructor(containerSelector:string){
            this.selector = containerSelector;
            this.setup();
        }

        setup():void{
            $(this.selector).css({
                width:'100px',
                height:'70px',
                'text-align':'center',
                'padding-top':'30px',
                background:'#ff0000',
                cursor:'pointer',
                'margin-bottom':'30px'
            })
            .click((e)=>{
                if(this.delegate && this.delegate.buttonClicked){
                    this.delegate.buttonClicked(this, e.pageX, e.pageY);
                }
            });

        }
    }

    export class Main implements IBasicButtonDelegate{
        mybutton:BasicButton;

        constructor(){
        }

        init():void{
            this.mybutton = new BasicButton('#button_container');
            this.mybutton.delegate = this;
        }

        buttonClicked(button:BasicButton, pageX:number, pageY:number):void{
            $('#logarea').append('<p>clicked pageX = ' + pageX + ' , pageY = ' + pageY + '</p>');
        }
    }
}

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>sample</title>
<script src="js/jquery-1.8.3.min.js" type="text/javascript"></script>
<script src="js/main.js" type="text/javascript"></script>
<script>
$(function(){
    var main = new kk.Main();
    main.init();
});
</script>
<style>
body{
    font-size:12px;
}
p{
    margin: 0 0 5px 0;
}
</style>
</head>
<body>

<div id="button_container">ボタンを<br>クリック</div>

<div id="logarea"></div>

</body>
</html>

自作iPhoneアプリ 好評発売中!
フォルメモ - シンプルなフォルダつきメモ帳
ジッピー電卓 - 消費税や割引もサクサク計算!

ページトップへ戻る