@ledsun blog

無味の味は佳境に入らざればすなわち知れず

JavaScriptのパターン シングルトンパターン

定義を確認

GoFの定義したSingletonパターンの目的(Intent)は以下の二つです*1

  1. あるクラスのインスタンスが1つしか生成されないことを保証する
  2. そのオブジェクトへのグローバルなアクセス手段を提供する

実装例

JavaScript デザインパターンの例がいけてます。 ただし、本には誤植があるのでLearning JavaScript Design Patternsの例を見てみましょう*2

var mySingleton = (function () {
  // Instance stores a reference to the Singleton
  var instance;

  function init() {
    var privateRandomNumber = Math.random();

    return {
      getRandomNumber: function() {
        return privateRandomNumber;
      }
    };
  };

  return {
    // Get the Singleton instance if one exists
    // or create one if it doesn't
    getInstance: function () {

      if ( !instance ) {
        instance = init();
      }
      return instance;
    }
  };
})();

init で生成したオブジェクトをキャッシュしています。 何回getIncentaceをしても同じオブジェクトを取得できます。

var singleA = mySingleton.getInstance();
var singleB = mySingleton.getInstance();

console.log( singleA === singleB ); // true
console.log( singleA.getRandomNumber() === singleB.getRandomNumber() ); // true

しかし、JavaScriptでは継承を禁止することはできません。 getIncetanceで取得したオブジェクト singleA をObject.createで継承してみましょう。

var singleC = Object.create(singleA);

console.log( singleA === singleC ); // false
console.log( singleA.getRandomNumber() === singleC.getRandomNumber() ); // true

なんと!異なるオブジェクトになったのに返す値は同じだ!!!

リビーリングモジュールパターンになっているためinit変数は不可視かつオーバーライド不可です。 単一性を保証するのはmySingletonオブジェクトではなく、instace変数にキャッシュしているオブジェクトなのです。

クラス教徒への罵倒

ただし getInstance というメソッド名はいけません。クラス教に惑わされています。 プロトタイプベースにはインスタンスオブジェクトとクラスオブジェクトの区別はありません。 単に getObject とすべきです。

クラス教に惑わされすぎるとJavaScript パターンに記載された読むものすべてを混乱に陥れる吐き気を催すおぞましいシングルトンパターンを書くようになります。*3

JavaScriptパターン あなたにプロトタイプ神のご加護がありますことを JavaScriptデザインパターン

*1:http://www.oodesign.com/singleton-pattern.html より

*2:一部省略しています

*3:そもそもシングルトンパターンが邪悪であることも忘れてはいけません。如何に邪悪であるかはシングルトンが邪悪な理由 - まつぼ x WebワタシはSingletonがキライだを参考にしてください。