Singleton。インスタンスの同一性を保証したい場合。「人口」プロパティを持つEarthクラスのインスタンスを例に書いてみる。
function Earth() {
this.population = 2;
}
// このクラス(静的/static)変数にインスタンスが保存される。
Earth.instance = null;
// このクラス(静的/static)メソッドで上のインスタンスを受け取る。
function Earth.getInstance() {
if (Earth.instance == null) {
Earth.instance = new Earth;
}
return this.instance;
}
function Earth.prototype.increasePopulation() {
this.population = this.population * 2; // 人口が2倍に増える
}
function Earth.prototype.getPopulation() {
return this.population;
}
// 実際に使用する場面1
var e1 = Earth.getInstance();
print("1st test: " + e1.getPopulation() + "(e1)"); // print は適当に定義するべし
// 実際に使用する場面2 (1とは違う場面)
var e2 = Earth.getInstance(); // 参照渡しなので結局 e2 = e1
e2.increasePopulation();
print("2nd tset: " + e2.getPopulation() + "(e2)");
// 再び場面1に戻る
print("3rd test: " + e1.getPopulation() + "(e1)");
// Result:
// 1st tset: 2(e1)
// 2nd test: 4(e2)
// 3rd tset: 4(e1) <- e1 の population も増えている!
たとえば、データベースの操作用クラスのインスタンスが複数出来てしまうと、トランザクションの途中経過がそれぞれ別のインスタンスに渡されてしまい、結果的に不整合が生じることがある。次の例で、db1とdb2がそれぞれ別にnewされたATMのデータベース操作用クラスのインスタンスだとすると、
このような事になってしまう。もちろん、コーディング中にみんなでdb1を使うことを徹底すれば問題ないが、大規模なプロジェクトだとどこかでミスが起こるかも知れない。そこで、インスタンスが1つであることを保証するSingletonパターンの出番になる。上のサンプルコードを見れば分かるが、Earth.instanceに実際に必要なインスタンスをキャッシュし、Earthクラスが必要になった場合は必ずEarth.getInstanceでそのインスタンスを得るようにしている。これにより、db1、db2がそれぞれnewされた場合と異なり、使用されるインスタンスは常に1つになる。
と、言いたいところだが、JavaScriptにはアクセス制限の概念がないのでJavaのようにはうまく行かない。e3 = new Earth()と書いても通ってしまうのだ。よって、言語レベルでの同一性保証は得られない。まあ、スーパーグローバルな変数を持たなくて済むと言う利点もあるので……。
関係ないけど、メソッドの定義はClass.prototype.method = function() {...}の方がスマートかなぁ。
次はStrategyでも書くかな。OOPの利点がハッキリ実感できるデザインパターンだと思う。
こちらで試した限りでは
function Earth() {
if (arguments.callee.caller != arguments.callee.getInstance) {
return arguments.callee.getInstance();
}
this.population = 2;
}
と呼び出し元の関数を確認することでnew Earthとされた場合でも唯一のインスタンスが返るようになりました。
ただしコンストラクタ関数内でreturnで返したオブジェクトがnew演算子の返り値になるという動作がECMAScriptに準拠したものかはわかりません。
問題点としてはarguments.calleeがIE5以下では使えないこと(これはEarthで代用可能)とOperaではarguments.calleeで参照されるオブジェクトにcallerプロパティが存在しないことでしょうか。
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/script56/html/js56jslrfFeatureInformation.asp
http://www.opera.com/docs/specs/js/ecma/
それからfunction Class.prototype.method() {...}という書き方はJScriptの方言であり、JavaScriptではエラーになります。
興味深い情報をありがとうございます。実のところ、ECMAScriptに関してはあまり詳しくないので、こういった情報を頂けると大変助かります。
コンストラクタ内でのreturnについてですが、どうも気持ち悪いですね。Javaなどでもコンストラクタはvoidが基本ですし。
メソッドの定義方法ですが、JScriptの方言だったとは知りませんでした。ECMA準拠の記述を調べてみたいと思います。
http://yudai.arielworks.com/memo/2005/02/24/020708.trackback
末尾に「8 + 2」の計算結果を繋げて下さい。例えば計算結果が「17」の場合、「020708.trackback17」です。これは機械的なトラックバックスパムを防止するための措置です。