2013年5月18日土曜日

JavaScriptで依存性を排除するためにデフォルトパラメータを導入する

JavaScriptのクラスHogeをテストしたいのだけれど、次のような状況でそもそもインスタンス化が大変な場合にどうするか? という話。
  • Hogeはコンストラクタ内で別のクラスFugaをインスタンス化してプロパティに設定している。
  • Fugaはインスタンス化の際にサードパーティ・ライブラリのAPIを大量に呼び出している。
  • サードパーティ・ライブラリは、開発環境では使えない(例えば、REST APIを提供するサーバがまだ立っていない)。
この場合をコード例で表してみる。サードパーティ・ライブラリが使えないため、Fugaのコンストラクタ呼び出しでエラーが発生し、Hogeをインスタンス化できないとする。
Hoge = function () {
  this.fuga = new Fuga();
  // ...
}
// ...
Fuga = function () {
  this.thirdPartyLibrary = new ThirdPartyLibrary();
  // ...
}
// ...

FugaがThirdPartyLibraryをラップしているから、テスト時にはテストダブルに入れ替えたい。でも、そもそもHogeもFugaもインスタンス化できない。

こんな状況に対応するためのリファクタリングとして、『レガシーコード改善ガイド』は25.14「コンストラクタのパラメータ化」か、デフォルト引数の追加を紹介している。オーバーロードのないJavaScriptでは、コンストラクタをパラメータ化できないので、デフォルト・パラメータを指定する。

デフォルト・パラメータがtrueと見なせる(false、null、0、""、undefinedのいずれでもない)なら、こんな風に短く書ける。||は真偽値ではなくてfugaを返す性質を利用している。
Hoge = function (fuga) {
  this.fuga = fuga || new Fuga();
  // ...
}
// ...

もっとロバストな書き方は次の通り。こちらは、デフォルト・パラメータがfalseと見なされる場合も使える。実用的なのは、デフォルト値が0やfalseの時。
Hoge = function (fuga) {
  if (typeof fuga === 'undefined') {
    fuga = new Fuga();
  }
  this.fuga = fuga;
  // ...
}
// ...

References



0 件のコメント:

コメントを投稿