2010年5月30日日曜日

GWTでGINを使う


GWTでGINを使う

概要

    Google GINでの分割モジュールロードを実装する

    実はまだ匿名状態やコンパイル済みのモジュールとしては一般化できていない。

    お互いのことを知らなくても各モジュールを使用できる、という部分について、

    GINを使った状態での統一モジュールがまだ無い。


    →結論としては、まだ配布されている状態でのLazyなモジュールロードシステムで、GINに組み込まれたものは無いようだ。

        GWT2.0でのモジュール分岐はあるんだけどね。IOの映像を見るべきだな。


    

サンプルを実行してみる

    GoogleCode Gin関連 からサンプルっぽいソースを拝借、実行。 SVNからソースをチェックアウトする。

    そのなかのSamples>sampleが良さそうなので、チェックがてら使ってみる。

    http://code.google.com/p/google-gin/source/checkout


    0、ビルドできない

    理由は単純、AsyncProvider<T> instance 関連のソースコードがGinに含まれてないんだけど、サンプルには含まれてる前提のビルドが仕込んである。

    GWTが1.0から2.0に移行したタイミング移行、手を加えられているみたいだ。

    調べてみると、言及してる方がいた。

    http://reminiscential.wordpress.com/

    

    /**以下、引用させていただく


    Someone contributed a patch to gin, which made split points transparent to the user of gin. The presenters that aren’t needed initially can be wrapped in an         

    AsyncProvider<T> instance – which by the blessing of deferred binding, translates into a GWT.runAsync call in the generated code. The patch hasn’t been 

    accepted into gin’s trunk yet, but it’s fairly easy to apply the patch and rebuild. A huge thanks to fazal.asim who hacked and contributed this patch.


    以上。*/


    という事で、AsyncProvider周りについては、まだDLして使えるような状態ではない。

    (記事に出てくる議論と改修が行われてテストコードが書かれたのが3月、ダウンロードできる状態のプロジェクトは1月のもの)

    まあ、こういうこともあるよね。

    幸い今回試したいのは、単純なInjectionなので、まあ気にしない。


    AsyncProvider周りは孤立している機能なので、もろっとコメントアウトして実行。

    →更新(F5)を行うたびに、文章が変わるだけのテストプログラムが完成。


    本来なら、WidgetにAsync(非同期)な機能が実装されて、Widgetに"非同期実行"、"非同期詳細表示"、"エラー表示" ボタンがついて、実行される、、はずなんだけど。

    




言葉の定義

    モジュール

        機能の実装を呼び出すための関連づけをbindというプログラミング結合手段でまとめたもの。

        Guice/GINの目的は、異なるクラス間で、クラスファイル=機能に関連性を持たせること。


        ・コードによる結合

            設定ファイルとコードの同時性が保たれない問題を、設定ファイル=実行ファイルと一緒にする事で解消した。


        ・何でもバインディング

            全く無関係のクラスだろうと、モジュールという枠でもって処理を合致できる。

            物議をかもしそうだ。

            AsyncCallbackもこれで実装されている。


        ・インターフェースが確立されたアクセス

            インターフェースと実装をつなぐ事により、機能内容へのアクセッサを確保する。




使い方

    下記1~3を用意する。


1.Ginjectorを拡張したインターフェース

    メソッドの実装(メソッド名と型の実装)の記述を行う。

    


    メソッドを記述する。

    public TestPresenter getTestPresenter();

    このメソッドを外部から呼んだ際、あたかもシングルトンクラスや

    他の関連するクラスから取得したように見せかける事が出来る。

    また、それらのコードを書かずに実装できる。(BoilerPlateを書かずにすむ)

    続けてこのインターフェースに、モジュールとの関連付けをするコードを書く。


    @GinModules( { GuiceClientModule.class })

    インターフェースの定義を行う。


    

    で、出来上がったコードは下記。


GuiceInjector.java

/**

 * Injectorインターフェースサンプル

 * @author sassembla

 *

 */

@GinModules(//かっこでの複数設定が可能、モジュールとのひも付け

{

    GuiceClientModule.class//次に作成するモジュール

})

public interface GuiceInjector extends Ginjector {


    public TestPresenter getTestPresenter();


}

    

    作成したインターフェースは、下記のように書く事でインスタンス作成が出来る。

    GuiceInjector injector = GWT.create(GuiceInjector.class);





2.AbstractGinModuleを拡張したモジュールクラス

    下記のような名前のクラスを作ってみる

    public class GuiceClientModule extends AbstractGinModule


    先ほど作成したインターフェースから使用するクラスの、ひも付けの実装を行う

    (=実際に使用する際の使用法記述をコードに書く事で行う、ととらえるとわかりやすい。)


    AbstractGinModuleで抽象実装されているメソッドを実装する。


    protected void configure() {

    }

    このメソッド内に記述されたバインド処理が、

    呼び出すクラスをどのように扱うかを規定している。


    メソッドにbind内容を記述。ただのプログラミング。

    bind(TestPresenter.class).in(Singleton.class);//クラスをシングルトンインスタンスとしてこのモジュールに結びつけ、シングルトンとして指定。


    TestPresenterクラスの扱いについて、シングルトンクラスであるように設定している。

    こうしておくと、Injectorを通してモジュールに書いてある内容通りに特定のクラスがインスタンス化され、

    以後好きなときに呼び出せる。

    (ここまで読んだだけで全体がわかるならエスニックパーマでは無いほうのエスパーだと思う。 

        もちろん、呼び出される方についての情報に続きがある。)


    いちおうこのクラスのソースは下記。


GuiceClientModule.java

/**

 * モジュール

 * @author sassembla

 *

 */

public class GuiceClientModule extends AbstractGinModule {

    @Override

    protected void configure() {

        bind(TestPresenter.class).in(Singleton.class);//クラスをシングルトンインスタンスとしてこのモジュールに結びつけ、シングルトンとして指定。

    }

}




3.呼び出されるクラス(さっきからちらちら出てきてるTestPresenterクラス)

    もちろん実態(というか実装)が存在する。この部位が、当たり前だけれど好きに用意してくれていいクラス。


    いきなりソース

/**

 * プレゼンターの存在するクラス

 * @author sassembla

 *

 */

public class TestPresenter {

    /**

    * このプレゼンターに設定されているクラス

    * @param s

    */

    public void say(String s) {

        System.out.println(this+":"+s);

    }

}



    動かしてみよう!

    適当なEntryPointのあるクラスで、下記を記述すると


    GuiceInjector injector = GWT.create(GuiceInjector.class);

    final TestPresenter appPresenter = injector.getTestPresenter();

    appPresenter.say("hello Gin!");


    →コマンドラインに下記出現。

    hello Gin!


    もちろん、これだけ見ると

    「は? なんにも得してなくね?」

    となると思う。


    でも実際、TestPresenterクラスがシングルトンを実装しなければいけないクラスだったり、

    WebGLや物理エンジン、サウンドエンジンなどのステートマシン的なプログラムを必要とする場合、

    それらにいちいちシングルトン実装なんて書いていられるかというと、私はかなり嫌だ。


    Ginを使った今回の例では、シングルトンか否かはたった1行しか書いていない。




具体的な使用法


0.簡単な使い方(シングルトン実装)

    上記の例と同じ。


    シングルトン実装のコードを書かずに、隠蔽できる。


    final TestPresenter appPresenter = injector.getTestPresenter();

    appPresenter.say("hello Gin!");

    シングルトン性質は、injectorを通して取得、アクセスする限り保持される。



1.DIな使い方(連携)

    bind(ConnectionInterface.class).to(ConnectionImpl.class).in(Singleton.class);

    InterfaceにImplクラスをひも付け、特定のインターフェースを搭載したクラスを読み出す事が出来る。

    インターフェースを実装しさえすれば、どんな実装モジュールでもすげ替える事が出来る。

    →アップデートやメンテナンスにいいんじゃなかろうか。


    前提

        impl 実装クラスに@Singletonアノテーションが必要。

        impl 実装クラスのコンストラクタに@Injectアノテーションが必要。



    限定条件

        一つのインターフェースからバインドできるのは、一つの実装だけ。

        出来るのは、インターフェースAを実装したaについて、

        Aとaをつなげ、ボイラーコードの存在を無くす事。

           

        一つのインターフェースに複数の実装をバインドしようとすると、実行時にエラーを出す。

        

    実装をさらに拡張したオブジェクトがどうなるかについて

        ちゃんとクラスの型が異なるため、CastExceptionを吐いて終了した。



2.より広範囲で高度な使い方(チェーンする)

    存在しているのは、


    1.Injector

    2.Module

    この2つだけだが、自前でさらに広範囲のバインドを内包、行う特殊なModuleを作る事も可能。

    たとえば、自前のアブストラクトなModuleクラスを作成、AbstractGinModuleを継承させておく。


    継承をしたモジュール内では、自分用のBinderを定義する事が可能。


    下記のように、自前のモジュールを作ったとする。

    public abstract class AbstractChainModule extends AbstractGinModule {


    public AbstractChainModule () {

        super();

    }


    /**

    * bInterfaceに対して、aInterface2とaImplをバインドしたものをバインドする。

    * 

    * bInterface - aInterface

    *                     +- aImpl になる。

    * 

    * @param <A>

    * @param bInterface    

    * @param aInterface2

    * @param aImpl

    */

    protected <A extends aInterface> void bindAB(

    Class<? extends bInterface> bInterface,//抽象的な(仮の)bインターフェースを拡張したインターフェース

    Class<A> aInterface2,//クラスAのインスタンス a、この時点でAaInterfaceを拡張したものなので、aはインターフェース(さすがにこのへんは暗黙)

            Class<? extends A> aImpl//Aのを拡張したインスタンス aImpl

            ) {

                bind( bInterface );//先ずbImplをバインドし、

                bindA( aInterface2, aImpl );//aインターフェースにaの実装をバインドする

    }



    /**

    * 入れ子実装

    * aInerfaceにaImplをバインドする。

    * 

    * @param <A>

    * @param aInterface

    * @param aImpl

    */

    protected <A extends aInterface> void bindA( Class<A> aInterface, Class<? extends A> aImpl ) {

        bind( aInterface ).to( aImpl );

    }


    入れ子構造にする事で、モジュール同士を連結させたり、内包させる事が可能。

    Guiceでは、サーバサイドでのモジュール寿命(一回ごとに毎回生成、キャッシュに関連づけて、、など)について、

    制御を行う旨がある。

    もうDIっていう範疇じゃない。

    この時点まででGoogleIO2009で発表された全コンディション情報の1/3程度でしか無い。


    GoogleIOの2010を見る人、行った人、是非お話聞かせてください。


    


3.サーバサイドとbind(RPC)

    ここから先に、GWTでもてはやされているRPC(Remote Procedure Call)がある。

    GINやGuiceが生まれた背景をしっかり調べると、けっこう楽しい事をしているのがわかってくる。

    言語と、その言語を実行する環境を使っての、スマートな”依存関係の解決”と”資産の最大化”に向かってるんだろうな。


    


注意点

    Developmentモードでの使い方について、Inject系の処理は一度モードを落とさないと、変更点が加味されない。原因は不明だが、キャッシュ系?

    →読み込まれているスクリプトの問題だった。リロードで解消したり。



今後の展開

    もう少しがんばると、LPC(Local Procedure Call)が出来そうな気がする。


    魔法の名前一致でセントラルを作る。セントラルからモジュールの枝葉をのばすと、

    お互いのモジュールの存在を知らなくても、通信できるようになる。

    わーい、iPhoneと同じ完全相互非依存プログラムが出来るぞー。

    DIとは全く別の話だー。


公開完了 -toru inoue 10/05/30 15:45 


0 件のコメント:

コメントを投稿

フォロワー