JavaScript未経験なFlex開発者にオススメしたいClosure Tools

2012.10.04

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

毎度お世話になっております。最近は「Dart」や「AngularJS」等に浮気をしておりました、クラスメソッドの稲毛です。

今回は、「JavaScript未経験なFlex開発者にオススメしたいClosure Tools」と題して、Webアプリケーション開発をFlexとClosure Toolsで比較してみたいと思います。何かとフリーダムで、どのように開発を進めていくのかイメージが湧きにくいJavaScriptのコーディングが、Closure Toolsによってどのように整理されるかを見て取って頂ければ幸いです。もちろんFlex開発者以外の方にもClosure Toolsはオススメですよ。(^^)

クラスベースOOP

Closure Libraryが持つ強力な依存性管理を用いることで、ActionScriptのように「1クラス=1ファイル」として管理することができます。記法にこそ差異はありますが、ActionScriptと同じような構成でクラスを表現することが可能です。

Carクラス

Car.as
package example {
public class Car {
    public function Car() {} // (1) Constructor
    public function horn():void { // (2) Abstract method
        throw new Error("unimplemented abstract method");
    }
    public function gas():void { // (3) Instance method
        trace("Speed up.");
    }
    public function brake():void { // (4) Instance method
        trace("Slow down.");
    }
}
}
car.js
goog.provide('example.Car');
goog.scope(function() {
  example.Car = function() {}; // (1) Constructor
  example.Car.prototype.horn = goog.abstractMethod; // (2) Abstract method
  example.Car.prototype.gas = function() { // (3) Instance method
    console.log('Speed up.');
  };
  example.Car.prototype.brake = function() { // (4) Instance method
    console.log('Slow down.');
  };
});

RoadRunnerクラス

「継承」や「スーパークラスのメソッド呼び出し」にもClosure Libraryが便利な関数を提供しています。

RoadRunner.as
package example {
import mx.controls.Alert;
public class RoadRunner extends Car { // (1) Extend class
    public function RoadRunner() { // (2) Constructor
        super(); // (3) Call super
    }
    override public function horn():void { // (4) Overriden method
        Alert.show("Beep! Beep!");
    }
}
}
roadrunner.js
goog.provide('example.RoadRunner');
goog.require('example.Car');
goog.scope(function() {
  example.RoadRunner = function() { // (2) Constructor
    goog.base(this); // (3) Call super
  };
  goog.inherits(example.RoadRunner, example.Car); // (1) Extend class
  example.RoadRunner.prototype.horn = function() { // (4) Overriden method
    window.alert('Beep! Beep!');
  };
});

従来通り「1クラス=1ファイル」としてリポジトリで管理できるので、複数人で開発にあたる場合にも有効ですね。 ※ちなみにロードランナーとはこんなクルマです。

スクリプトへのエントリーポイント

スクリプトへのエントリーポイントというと、Flexではルートアプリケーションの「creationComplete」イベントハンドラ、HTMLではBody要素の「load」イベントハンドラだったりするのですが、Closure Libraryではクラスのインスタンス生成を行った際のコンストラクタをエントリーポイントとする方法が一般的なようです。 なんとなく「Flexでいえば『IMXMLObject』インタフェース実装によるViewHelperに似ているかなぁ」と思ったのでそれと比較しています。

View

それぞれエントリーポイントとして用意したクラスのインスタンスを生成している点が同じですね。

Main.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
               xmlns:s="library://ns.adobe.com/flex/spark" 
               xmlns:mx="library://ns.adobe.com/flex/mx"
               xmlns="example.*"
               creationComplete="app.creationCompleteHandler(event);">
    <fx:Declarations>
        <App id="app" /><!-- (1) Instantiation -->
    </fx:Declarations>
    <s:Button id="myButton"
              label="Push" />
</s:Application>
main.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Main</title>
    <script type="text/javascript" src="../closure-library/closure/goog/base.js"></script><!-- ※Core script -->
    <script type="text/javascript" src="scripts/deps.js"></script><!-- ※Dependency script -->
    <script type="text/javascript">
      goog.require('example.App');
    </script>
  </head>
  <body>
    <input id="myButton" type="button" value="Push">
    <script type="text/javascript">
      new example.App(); // (1) Instantiation
    </script>
  </body>
</html>

Logic

エントリーポイントとなるクラスではボタンに対してイベントハンドラを設定しています。

App.as
package example {
import flash.events.MouseEvent;
import mx.core.IMXMLObject;
import mx.events.FlexEvent;
public class App implements IMXMLObject {
    private var view:Main;
    public function initialized(document:Object, id:String):void {
        view = document as Main;
    }
    public function creationCompleteHandler(event:FlexEvent):void {
        view.myButton.addEventListener(
            MouseEvent.CLICK,
            myButton_clickHandler
        ); // (1) Add click event listener
    }
    protected function myButton_clickHandler(event:MouseEvent):void { // (2) Event handler
        var car:Car = new RoadRunner();
        car.gas();
        car.brake();
        car.horn();
    }
}
}
app.js
goog.provide('example.App');
goog.require('goog.dom');
goog.require('goog.events');
goog.require('example.RoadRunner');
goog.scope(function() {
  example.App = function () {
    goog.events.listen(
      goog.dom.getElement('myButton'),
      goog.events.EventType.CLICK,
      this.myButton_clickHandler
    ); // (1) Add click event listener
  };
  example.App.prototype.myButton_clickHandler = function(event) { // (2) Event handler
    var car = new example.RoadRunner();
    car.gas();
    car.brake();
    car.horn();
  };
});

コンパイル

Closure Compilerは依存関係にあるJSファイルを全て統合し、最適化が施されたJSファイルを生成します。ちょうどmxmlcによってFlexのソースコードがSWFファイルにコンパイルされるような感じです。

main.html(コンパイル済みJSファイルを使用する場合)
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Main</title>
  </head>
  <body>
    <input id="myButton" type="button" value="Push">
    <script type="text/javascript" src="scripts/compiled.js"></script>
    <script type="text/javascript">
      new example.App();
    </script>
  </body>
</html>

ひとつに統合されたJSファイルには、使用されているClsoure Libraryのコアソースも含まれているので、本当にそのファイルだけを読み込むだけで良いのです。極めてシンプルですよね。

まとめ

いかがでしたでしょうか?極めて簡単なアプリケーションでの比較なので、これで全てというわけではありませんが、「これならHTMLベースのWebアプリケーション開発ができそうかな。」と、なんとなくでも感じて頂けたなら幸いです。(^^)

Closure Toolsの使い方については過去の記事をご覧ください。併せてソースコードをGitHubに用意しましたので自由にご利用ください。

Flex(FlashBuilderプロジェクト)
https://github.com/inage-toru/closure/tree/master/example-blog-20121004/example-flex
Closure(Eclipse静的Webプロジェクト)
https://github.com/inage-toru/closure/tree/master/example-blog-20121004/example-closure ※別途Closure Libraryを取得する必要があります。取得方法については「closure-library」フォルダ内のREADME.txtを参照ください。