指向性メモ::2005-02-24::JavaScriptでデザインパターンその2

ページ情報
制作日
2005-02-24T02:07:08+09:00
最終更新日
2005-02-24T09:52:53+09:00
ページ内目次

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の利点がハッキリ実感できるデザインパターンだと思う。

Comments

Name
ANONYMOUS
Datetime
2005-02-24T09:43:06+09:00
Message

こちらで試した限りでは

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ではエラーになります。

Name
石川
Datetime
2005-02-24T09:52:53+09:00
Message

興味深い情報をありがとうございます。実のところ、ECMAScriptに関してはあまり詳しくないので、こういった情報を頂けると大変助かります。

コンストラクタ内でのreturnについてですが、どうも気持ち悪いですね。Javaなどでもコンストラクタはvoidが基本ですし。

メソッドの定義方法ですが、JScriptの方言だったとは知りませんでした。ECMA準拠の記述を調べてみたいと思います。

Trackbacks

Trackback Ping URI

http://yudai.arielworks.com/memo/2005/02/24/020708.trackback

末尾に「8 + 2」の計算結果を繋げて下さい。例えば計算結果が「17」の場合、「020708.trackback17」です。これは機械的なトラックバックスパムを防止するための措置です。

Post a comment

Name (optional)
Email address or URI (optional)
Do the math below (required to filter comment spams)
8 + 2 + 6 =
Message (required)
Submit
連絡先、リンク、転載や複製などについては『サイト案内』をご覧ください。Powered by HIMMEL

I ♥ Validator