WebOS Goodies

WebOS の未来を模索する、ゲームプログラマあがりの Web 開発者のブログ。

WebOS Goodies へようこそ! WebOS はインターネットの未来形。あらゆる Web サイトが繋がり、共有し、協力して創り上げる、ひとつの巨大な情報システムです。そこでは、あらゆる情報がネットワーク上に蓄積され、我々はいつでも、どこからでも、多彩なデバイスを使ってそれらにアクセスできます。 WebOS Goodies は、さまざまな情報提供やツール開発を通して、そんな世界の実現に少しでも貢献するべく活動していきます。
Subscribe       

Closure Library を使うべき 10 の理由

先週 Closure Library で構築したドローウィジェット Closure Draw を公開しましたが、はてぶ数などを見る限りさほど多くの反響はなかったようです。まあ、機能が中途半端だったり作りが甘かったりというところが大きいのは間違いありませんが、 Closure Library 自体がまだメジャーではないのもあるのかな、とも思います。実際、ぐぐってもファーストインプレッション的なものがほとんどで、活用情報は少数(ありえるありあさんWEB開発メモさんなど)です。

しかし、これは少し残念な状況です。私も最近ようやく気付いたのですが、 Closure Library は便利機能の詰まった単なるライブラリではなく、 JavaScript 開発のスタイルを大きく変化させて大規模な Web アプリケーション開発に対応させるための、意欲的かつ重要なプロジェクトです。これまでの「いかに手軽に使えるようにするか」というトレンドを乗り越えて、「複数人の開発チームでいかに体系的な開発を行うか」を重視した初めての JavaScript フレームワークなのです。今後予想される本格的な Web アプリケーション時代に向けて、この開発スタイルを経験しておかないのは、大きな損失です。

そこで、そういった開発スタイルを変革させる特徴も含めて、 Closure Library の魅力を 10 項目にまとめてみました。 Closure Library をご存じない方、「JavaScript フレームワークは jQuery だけでじゅうぶんだよね」と思っている方(少し前まで私も思ってました)、ぜひ読んでみてください。

1. 豊富な UI コンポーネント

まずもっともわかりやすい魅力は、豊富に用意された UI コンポーネントです。各種ボタンから複雑な UI ウィジェットまで、およそ必要と思われるものはほとんど揃っています。例えば以下はリッチテキストエディタのウィジェットである goog.editor のデモの画面です。

ソースを見ると、これだけのリッチテキストエディタが非常に簡単なコードで実現できています。また、各機能がプラグイン化されており、アプリケーションに合わせて柔軟にコンフィギュレーションできるのもわかりますね。

Closure Library には、このような優れた UI コンポーネントが多数含まれています。こちらにあるデモをひと通り見てみれば、その多彩さがお分かりいただけるでしょう。

2. Closure Compiler による最適化とエラーチェック

Closure Draw の記事でも触れたとおり、 Closure Library のスクリプトは Closure Compiler を利用して圧縮するのが基本です。しかし、この Closure Compiler はただサイズを削減するだけのツールではありません。 JavaScript のすべての実行パスをトレースし、エラーチェックと最適化を行う、まさに「Compiler」と呼ぶに相応しい強力なツールなのです。

実例を示しましょう。以下のような JavaScript コードがあるとします。

/** @constant */
var PI = 3.141592;

function unnecessary() {
  alert('This function is never called.');
}

function printSin(angle) {
  alert(Math.sin(angle*PI/180.0));
}

printSin(90);

これを Closure Compiler に(ADVANCED_OPTIMIZATIONS 有効で)通すと、こうなります。

alert(Math.sin(1.570796));

なんと、すべての関数や変数が削除されて、たった一行のスクリプトになってしまいました。いったいなにが起きたのでしょう。

Closure Compiler は、与えられた JavaScript 全体を解析し、その実行パスをトレースします。そして、決して実行されないコードがあればそれを削除し、短い関数は呼び出し元にインライン展開します。この機能によって unnecessary() は実行されないコードと判断され、 printSin() はインライン展開されて、いずれも削除されてしまったわけです。

さらに Closure CompilerJsDoc ベースのアノテーション・コメントによって変数の型などの属性を解釈します。変数 PI の定義行の上にある「/** @constant */」というコメントがそれで、これにより PI は定数として解釈されます。結果として「angle*PI/180.0」は「90*3.141592/180.0」という定数式に変換され、その計算結果である「1.570796」で置き換えられたのです。まさに C++ コンパイラ並の強力な最適化ですね。

次はエラーチェックの例を挙げます。以下のコードを Closure Compiler にかけると・・・

/** @constructor */
function Foo() {
  /** @type {number} */
  this.value = 0;
}

var foo1 = Foo();
var foo2 = new Foo();
foo2.value = "string";

2 つの警告メッセージが表示されます。

JSC_CONSTRUCTOR_NOT_CALLABLE: Constructor function (this:Foo): ? should be called with the "new" keyword at line 7 character 14
var foo1 = Foo();
              ^
JSC_TYPE_MISMATCH: assignment to property value of Foo
found   : string
required: number at line 9 character 11

最初の警告は(/** @constructor */ により)クラスのコンストラクタと宣言されている Foo() を通常関数として呼び出していることを、 2 つめは(/** @type {number} */ により)数値型と宣言されている変数に文字列を代入しようとしていることを指摘しています。このように、 Closure Compiler を通すことで従来は実行時にしかわからなかったエラーを事前に検出でき、大幅なコード品質の向上が期待できます。また、コメントに実質的な意味を持たせることで、それを記述するモチベーションを高めているのも見逃せない点ですね。

もちろん Closure CompilerClosure Library 以外のフレームワークでも利用可能です。しかし、とくに ADVANCED_OPTIMIZATION の適用にはけっこうきつい制約があり、既存のコードは動作しない可能性があります。また、すべての機能を活用するためにはアノテーション・コメントが必須となるため、 Closure Library との組み合わせが最も有効なのは間違いありません。

そしてもうひとつ、 Closure Compiler を使うことの大きなメリットがあります。それは、(実行速度を気にせずに)メンテナンス性の高いコードを書くことができるという点です。これまでの JavaScript 開発では最適化を手作業で行っていたので、プログラマーは実行速度とメンテナンス性の間で常に難しい判断を迫られてきました。しかし、これからはそのような手法は徐々に時代遅れとなり、メンテナンス性重視でコードを書いて最適化は Closure Compiler に任せるという開発スタイルが徐々に普及していくでしょう。また、そうなるべきだと思います。

3. 体系化された API

Closure Library は、まるで Java のクラスライブラリのごとく階層的な名前空間で体系化されており、その規模に比べると非常に見通しの良い構造を持っています。ライブラリ全体は名前空間 goog に納められており、さらに機能ごとに細かくモジュール化されています。

例えば先のリッチテキストエディタのクラスは goog.editor.Field であり、各機能を実装したプラグインはさらに深い goog.editor.plugins.〜 になります。 JavaScript を熟知している方はご存知かと思いますが、従来はこのようにネストした名前空間は実行速度低下の原因になり、避けるべきとされていました。しかし Closure Library では Closure Compiler がネストした名前空間をフラットなものに変換してくれるため、速度低下を気にせず理想的なモジュール化がなされています。

さらに重要なことは、 Closure Library のインフラに乗ることでアプリケーション側でも同様な体系化が可能になることです。 Closure Compiler による最適化や後述の依存性管理を活用して、アプリケーションに管理しやすい構造を持たせることができます。このことは、大規模 Web アプリケーションの多人数開発を強力にバックアップしてくれることでしょう。

4. 高度な依存性管理

上述のように Closure Library は体系化された構造を持っており、モジュールごとに個別の .js ファイルに分割されています。そして、それらの多くが別のモジュールを呼び出しており、全体として複雑な依存関係を構成しています。もしそれらの依存関係をプログラマーが管理しなければいけないとしたら、大きな負担となるのは容易に想像できるでしょう。

この問題を解決するため、 Closure Library には依存関係を自動処理する機能が実装されています。具体的には、 goog.require()goog.provide() という 2 つの関数を使うことで実現しています。例えば、前述のリッチテキストエディタを利用する場合、スクリプトの先頭に必ず以下の goog.require() を記述します(実際にはプラグインを利用するでしょうから、それらについても個別に goog.require() が必要です)。

goog.require('goog.editor.Field');

こうすると、リッチテキストエディタに必要な .js ファイルすべてが自動的に読み込まれます。それぞれのモジュールがどのファイルに依存するか、プログラマーが意識する必要は一切ありません。

また、自分で再利用可能なモジュールを作成した際は、その .js ファイルの先頭に以下の行を入れることにより、管理対象に含めることが可能です。

goog.provide('mymodule.Myclass');

もちろん、この行に続けて goog.require() を並べれば、 Mymodule.myclass が require された際にそれらも読み込まれます。そしてリリース時には、(Closure Draw の記事でも触れたとおり) calcdeps.py スクリプトによって必要な .js ファイルを統合・最適化し、ページ読み込みを高速化できます。

ちなみに、この依存性管理機能にはカラクリがあり、正しく機能させるためにはあらかじめ依存関係データベースを格納したファイルを生成しておく必要があります。 goog.require() はそれを参照して、どのファイルを読み込むべきかを判断しているわけです。

5. 充実したリファレンス

前述の Closure Compiler による最適化を活用する意味もあり、 Closure Library の各クラスや各メソッドには JsDoc 形式のコメントが記述されています。そして、そこから自動生成された、非常に充実したリファレンスマニュアルが公開されています。これは Closure Library のソースツリーにも含まれており、オフラインでも参照可能です。なんと、検索窓もオフラインで機能します。

Closure Draw の開発でも、これらのリファレンスはとても役立ちました。関連する項目へのリンクが豊富に容易されており、ソースコードもリファレンスから直接参照できるので、効率的に開発が行えます。

対照的に、残念ながら初心者向けのドキュメントはあまり充実していません。このあたりが Closure Library をとっつきにくいものにしている原因のひとつでしょう。 Google Code のドキュメントはもう少し頑張ってほしいものです。

6. 統合されたテストフレームワーク

最近の JavaScript フレームワークではテストの機能が重要になってきていますが、もちろん Closure Library にも実装されています。以下は Closure Library 自身のテストを走らせている様子です。いくつか失敗してますが・・・ ^^;

以前このブログでもご紹介した JsUnit をベースとしており、ほぼ同じアサーション関数が使えます。さらに goog.testing モジュールには AsyncTestCase とか Mock〜 とかいったクラスが多数あるので、かなり期待できそうです。私もまだあまり使えていないのですが、ぜひマスターして記事にしたいと思っています。

7. ブラウザ間の差異を吸収

ブラウザ間の仕様の違いを吸収するというのは、 JavaScript フレームワークの重要な役割のひとつです。 Closure Library は Gmail や Google Docs などで利用されているというだけあって、非常に広範囲な機能の抽象化に対応しています。以下に主なモジュールを挙げておきます。

  • dom (HTML の DOM ツリーの操作)
  • xml (XML ドキュメントの読み込みや生成)
  • cssom (CSS の操作)
  • json (JSON の生成・解析など)
  • events (DOM イベント)
  • selection (テキストボックスの選択範囲操作)
  • range (HTML の選択範囲操作)
  • history (ブラウザの履歴の管理)

そしてもちろん、 Closure Draw で利用している goog.graphics も SVG, canvas, VML を抽象化して、多数のブラウザでグラフィクス描画を可能にしています。これらの機能をクロスブラウザ(とくに IE)に対応させるには多くのノウハウと工数が必要ですが、 Closure Library を利用することで、それらのコストの多くを回避できます。

8. 多彩な AJAX 技術に対応

Google は高度な Web アプリケーションの基礎技術である AJAX を確立させた企業として有名ですが、 goog.net モジュールにはその Google の AJAX 技術がこれでもかと詰め込まれています。 XmlHttpRequest の様々なバリエーションはもちろんのこと、 iframe を利用したものや JSONP 、さらには詳細不明の BrowserChannel (GWT の BrowserChannel と同じもの?)や CrossDomainRpc などなど、あらゆる用途に対応できる機能が揃っています。

また、 AJAX ではありませんが、クライアントサイドでのフレーム間クロスドメイン通信を行う goog.net.xpc も今後は重要になってきそうな機能です。 HTML5 の postMessage() や iframe ポーリングなどの技術を利用して、ブラウザを気にせずに通信が行えるようになっています。

こういった Google のノウハウが簡単に利用できるというだけでも、 Closure Library を試す価値はじゅうぶんにあるでしょう。

9. 豊富なコレクションクラス

goog.structs モジュールには、汎用的に使える豊富なコレクションクラスが用意されています。含まれているクラスは以下の通り。

また、 goog.structs モジュール直下には各種コレクションで共通に使える操作関数(C++ STL でいう algorithm のようなもの?)がまとめられています。 JavaScript のフレームワークで、コレクションクラスがこれだけ重視されているのは珍しいのではないでしょうか。このあたりからも Closure Library が Gmail などの実際の Web アプリケーションで磨かれた実用性重視のフレームワークであることが伺えます。

10. 国際化サポート

日本人にとって、国際化サポートはライブラリ選択時のポイントのひとつですが、もちろん Closure Library はこのあたりへの対応もぬかりありません。 goog.i18n モジュールに国際化をサポートするクラス群があり、日付や数値のフォーマット変換などが簡単に実現できます。また、 UI 周りのテキストはほとんどが goog.getMsg() メソッドを通しているので、それを上書きすることで差し替えが可能です(Google 社内にはもっと進んだ仕組みがあるようですが、残念ながら公開されていません)。

さらに、一部の UI ウィジェットも個別に国際化対応がなされています。例えば goog.ui.DatePicker なら、以下の HTML で

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <link rel="stylesheet" type="text/css" href="closure-library/closure/goog/demos/css/DatePicker.css">
    <script type="text/javascript" src="closure-library/closure/goog/base.js"></script>
    <script type="text/javascript">
      goog.require('goog.dom');
      goog.require('goog.date');
      goog.require('goog.i18n.DateTimeSymbolsExt');
      goog.require('goog.ui.DatePicker');

      function initialize() {
        var datepicker = new goog.ui.DatePicker(null, goog.i18n.DateTimeSymbols_ja_JP);
        datepicker.create(goog.dom.$('datepicker'));
      }
      goog.exportSymbol('initialize', initialize, window);
    </script>
  </head>
  <body onload="initialize();">
    <div id="datepicker"></div>
  </body>
</html>

このように日本語のカレンダーが表示できます。

Gmail などで利用されているというだけあって、 Closure Library の国際化対応はかなり進んでいるという印象を受けます。これなら日本向けのアプリケーションでも安心して採用できますし、もちろんグローバル市場で勝負する際にも強力な武器となるでしょう。

以上、本日は Closure Library の特徴をご紹介しました。今後 HTML5 の普及に伴い、クライアントサイドのロジックが複雑化していくのは避けられない傾向でしょう。それに対応するためには、 Closure Library の開発スタイルが非常に有効です。私もまだまだ使い始めたばかりなので、今後も調査を続けて記事にしていこうと思っています(専用のカテゴリーも作りました :D)。皆さんも、ぜひ使ってみてください!

関連記事

この記事にコメントする

Recommendations
Books
「Closure Library」の入門書です。
詳しくはこちらの記事をどうぞ!
Categories
Recent Articles