Hatena::ブログ(Diary)

naoyaのはてなダイアリー

August 13, 2005

prototype.js でデザインパターン - Singleton

次は「たった1つのインスタンス」Singleton パターンです。あるクラスがあって、そのクラスのインスタンスは実行アプリケーションのライフサイクルを通じて唯一に制限したい、何回生成しても同じインスタンスである、というものです。

var Main = Class.create();
Main.prototype = {
    initialize : function() {},
    main : function() {
        document.writeln('Start.<br>');
        var obj1 = Singleton.getInstance();
        var obj2 = Singleton.getInstance();
        if (obj1 == obj2) {
            document.writeln('obj1 と obj2 は同じインスタンスです。<br>');
        } else {
            document.writeln('obj1 と obj2 は同じインスタンスではありません。<br>');
        }
        document.writeln('End.');
    }
}

というクライアントがありますが、Singleton.getInstance() で取得した Singleton クラスのインスタンスは同一であるはずなので、実行結果としては

インスタンスを生成しました
Start.
obj1 と obj2 は同じインスタンスです。
End.

となる、ということになります。

さて、Singleton パターンの実装ですが JavaScript で実装すると

var Singleton = Class.create();
Singleton.prototype = {
    initialize : function() {
        document.writeln('インスタンスを生成しました<br>');
    }
}
Singleton.singleton = new Singleton();
Singleton.getInstance = function() { 
    return Singleton.singleton;
}

となるでしょうか。ざっとみてみたところ prototype.js ではクラスメソッドの定義方法は特に特別なシンタックスはなく、prototype.js を使わない場合と同じくで

Singleton.getInstance = function() { 
    return Singleton.singleton;
}

と、クラスのプロパティに直接関数を定義してやる、ということになりそうです。

とまあ、これでクライアントが期待しているところと同じ動作にはなるのですが、ちょっと微妙ですね。Java だとコンストラクタを private にすることで new によるインスタンス生成を禁止することができるし、Perl ではコンストラクタを自前で実装するので new の中でインスタンスが一つになるようなロジックを組み立てて、インスタンスが唯一であることを保証できます。

このコードでは new Singleton とされると Singleton インスタンスが新たに生成されてしまうので、インスタンスを一つに縛ることができません。インスタンス生成には getInstance を呼ぶようにという性善説に基づくしかないと。

うまい具合に工夫してやってインスタンス生成を制限できないものかとちょっと試行錯誤してみましたが、いまのところいい方法は思いついていません。指向性メモ::2005-02-24::JavaScriptでデザインパターンその2でも同じことが書かれているなあ。(と、思ったらコメントにそれらしきものが。)

oooo 2005/08/14 00:12 Singleton = new Singleton();
としてあげれば、それ以降インスタンスの生成はできないですよ。
var s = new Singleton(); // <- エラー

naoyanaoya 2005/08/14 02:54 あ、なるほど。クラスのシンボルをインスタンスで上書きしちゃうわけですか。

どうもです。

oooo 2005/08/14 09:59 以下のようにするとインスタンスが生成できてしまいました。
var Singleton2=Singleton.constructor;
var s2=new Singleton2();
単に、
var s2=Singleton.constructor();
すると、コンストラクタは実行されますが、
s2は、undefinedです。(Firefoxの場合)

oooo 2005/08/14 10:06 何度もゴメンナサイ!
単純にこう書けばよかったです。
var s1=new Singleton.constructor();