定義を確認
GoFの定義したSingletonパターンの目的(Intent)は以下の二つです*1。
- あるクラスのインスタンスが1つしか生成されないことを保証する
- そのオブジェクトへのグローバルなアクセス手段を提供する
実装例
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
*1:http://www.oodesign.com/singleton-pattern.html より
*2:一部省略しています
*3:そもそもシングルトンパターンが邪悪であることも忘れてはいけません。如何に邪悪であるかはシングルトンが邪悪な理由 - まつぼ x WebやワタシはSingletonがキライだを参考にしてください。