"Scaling A JavaScript Codebase"の翻訳
2011/03/04(金) 16:28 Javascript親記事へこのエントリーをはてなブックマークに追加

この文章は"Scaling A JavaScript Codebase"の著者であるyeungdaさんの許可を貰い訳した文章です。
スピリチュアル翻訳ですので正確な文章は原文の方を読むと良いです。
翻訳許可を下さったyeungdaさんに感謝を
Scaling A JavaScript Codebase
http://yeungda.com/2010/02/26/scaling-a-javascript-codebase....
Scaling A JavaScript Codebase
これは私のチームがどのようにJavaScriptコードのスケール化について学んだかについてのお話です。この例が、大規模なJavaScriptコードベースを構築する必要があるが、それをどうやって行うべきか確信を持てないという開発者に自信を与えられる事を願っています。

The Big Ball of Mud

最初はui.jsと呼ばれていた一つのファイルから始まった。それはBig ball of mudパターンと言われるものだったが、それは何も抽象化されていなかったのでスケールすることができなかった。私たちはよりよい抽象化を求めてカプセル化モジュール化について読んだが、それらのアイデアをどう適応するのかは明らかではなかった。

時間がたつにつれてこれらのアイデアを適応する方法と、Javaコードで見つけたDependency Injectionやcompositionパターンといった一般的なアイデアと合わせる事を学んだ。
そして私たちは大きな泥のボール(big ball of mud)から離れて、一様構造で多くの抽象概念を持ったものへと移行した。全てはnamespaceから始まった。

Namespace

名前空間を使い、グローバル変数の使用を減らす事で3rd partyライブラリを安全に使えるようになる。グローバルスコープに名前空間となる一つのグローバル変数を作成し、それ以外にはグローバル変数は使わない。モジュールはこの名前空間のプロパティとなる。
各JavaScriptファイルの先頭で次のような名前空間の宣言をする事で行った。
var AppNameSpace = this.AppNameSpace || {};

Modularisation

次のパターンはカプセル化とモジュール化を使っている。
AppNameSpace.Thing = function(dependencies) {

    var anotherThing = dependencies.anotherThing;
    var aPrivateVariable = 'world';

    function aPrivateMethod() {
        return 'hello';
    }
    function aPublicMethod() {
        return aPrivateMethod() + ' ' + aPrivateVariable;
    }

    return {
        aPublicMethod: aPublicMethod
    }
}

このコードにオブジェクト指向のクラスとの多くの類似点を見いだす事ができる。
AppNameSpace.Thing()を呼ぶ事はクラスのコンストラクタを呼び出すようなものです。またコンストラクタに依存関係を渡すことで依存性を適応することができる。(今回はarugmentsとして渡したが、オブジェクトとして渡した方がスケール的にはベター) そしてこのコンストラクタの結果はパブリックメソッドのインスタンスのようになる。クロージャスコープによってパブリックメソッドは依存関係にあるものやプライベートメンバにアクセスすることができる。

一般的には各モジュールはモジュール名と同じファイルで見つけることができる。AppNameSpace.Thingならthing.jsとなるように。これによって簡単にモジュールを見つけることができる。物事はあなたが期待する場所にあるとうまく進みます。

Inheritance / Composition

JavaScriptは継承や合成を行える柔軟性がある事がわかったが、私たちは合成のみを使うことにした。次の例は画面(screen)の抽象化と、実際のそれぞれの表示する画面を抽象化したものです。
AppNameSpace.Screen = function() {
    return {
        show: function() {...}
    }
}

AppNameSpace.WelcomeScreen = function(dependencies) {
    var screen = dependencies.screen;

    return {
        show: screen.show
        welcome: function() {...} 
    }
}
WelcomeScreenはこのように作れる。
var welcomeScreen = AppNameSpace.WelcomeScreen({screen: AppNameSpace.Screen()});

Main

メインではモジュールを繋ぐ一つのファイルを作成します。Mainと呼ばれるものはアプリケーションのエントリーポイント(入り口)なります。
AppNameSpace.Main = function() {
    var dependency = AppNameSpace.Dependency();

    var aThing = AppNameSpace.Thing({
        dependency: dependency 
    });
}
ユニットテストでアプリケーション全体から始めることを避けるために、MainはJavaScriptファイルがロードし終わった後にアプリケーションページ書かれているところから呼ばれます。HTMLの最後に次のような記述を入れます。
<script type="text/javascript">AppNameSpace.Main();</script>

Which Abstractions?

最初は抽象化がどのように見えるのかを知ることは困難だった。まずはui.jsを小さなピースにばらすところから始め、コードの大部分に関して良い経験則を発見しました。私たちはUIを構築していたので、スタックにそれぞれの実際に表示する画面を抽象化したものをつんでいくという戦略をとりました。それぞれ progress indicator, screen, welcomeScreen, dialog, pleaseWaitDialogのような名前を使っていた。また表示されるものではないがhttp, form, tracking, validatorなどもモデル化した。

Compilation

モジュールの数が増えるにつれて、ブラウザが受信するファイル数は増えていきます。そのため、ビルド時にJavaScriptモジュールを連結して単一のファイルとして提供できるにした。同時にコードを圧縮する事もできたが、それは生産支援を妨げる可能性があると感じたため行わなかった。
全てのスクリプトがロードされるまで何も実行されないことの利点は、JavaScriptファイルを結合するときの結合順が問題にならないということだ。そのため、結合はシンプルに行うことができました。

Unit Testing

オブジェクト指向言語のユニットテストをいう考えをJavaScriptにも適応するべきだと考え、
私たちはjs-test-driverJasmineを利用しました。

The DOM

WebアプリケーションでDOMを避けることは困難です。私たちはどのようにやれば上手くできるかについては完璧な答えは出すことはできなかったが、大抵の場合においてはDOMを抽象化して、独立したレイヤーに分けることは良いことだと考えられた。
これはDOMへの依存を最小限するにはいいアイデアでした。(DOMがうまく動くために何度もテストするのはきつかった)

It’s surprising how far you can get with a namespace, modularisation, dependency injection, composition and unit testing.(なんて訳せば良いのかわからなかった)

名前:  非公開コメント   

  • TB-URL  http://efcl.info/adiary/099/tb/