Javascriptでデザインパターン (その1: Singleton)
きっかけ
現状、"prototypeとか即時関数とか、そーいう決まりは分かったけど、じゃあこれって具体的にどんなケースで使うの?どううれしいの?"が分からないので
(Javaで言えば、interfaceとabstract classの使い分けタイミングとか、そもそもinterfaceってどんなときに使えばうれしいの?が分からなかった)、
ひとまずJavascriptでのデザインパターンを勉強してみようということになりました。
ということで、まずは一番シンプルでとっつきやすいSingletonパターンから。
そもそもシングルトンパターンって?
”そのクラスのインスタンスが1つしか生成されないことを保証することができる。 ロケールやLook&Feelなど、絶対にアプリケーション全体で統一しなければならない仕組みの実装に使用される。(wikipediaより)”ですが、実際に使うときは、
・そもそもステート(=メンバ変数)を持たないこと(ステートを持ったら、それはただのグローバル変数ですね)
・インスタンスを1つしか持たない必然性があること(ウェブサイトによっては、”ポリモーフィズムが絡むこと”、という記述もありましたが、僕としてはもう少し抽象的な”必然性”という言葉の方がしっくりきます。ポリモーフィズムが絡まなくても、HTTPへのアクセスやDBのアクセスなど、ここは必要だよね、という部分はありそうな気がするので)
かと思います。ので、Javascript的に言えば、
・インスタンスが複数作られないこと
・メンバ変数を外からいじられないこと
の2つが要件になりそうです。
ということで、実際のコード
(お勉強のため、回りくどいことをしています。ご了承を・・・)
まずはダメコードから。オブジェクトリテラルで(それっぽいものを)書いたものです。
//Use Object literal var singleton_object_literal = { id: 1, name: "test" }; ObjectLiteral = function(){ console.log("ObjectLiteral"); console.log(singleton_object_literal.id); // 1 console.log(singleton_object_literal.name); // test singleton_object_literal.id = 2; console.log(singleton_object_literal.id); // 2 }; ObjectLiteral();
変数がすべてグローバル変数なので、idの値が外から書き換えられてしまいます。
Java的にいえば、このidは"public int id = 1"という感じですかね。
続いて、クロージャを使った(それっぽい)パターン
// Use Closure var singleton_closure = (function(){ return { id : 1, name : "test", getId : function(){ return this.id; }, getName : function(){ return this.name; } }; }()); closure = function(){ console.log("Closure"); console.log(singleton_closure.getId()); // 1 console.log(singleton_closure.getName()); // test singleton_closure.id = 2; console.log(singleton_closure.getId()); // 2 }; closure();
これも、idの値が外から書き換えられてしまうので、NGです。
ということで、クロージャを使いつつ、変数をPrivateで持ったケースです。
// Use Private value var singleton_private = (function(){ var id = 1; var name = "test"; return { getId : function(){ return id; }, getName : function(){ return name; } }; })(); privateValue = function(){ console.log("PrivateValue"); console.log(singleton_private.getId()); //1 console.log(singleton_private.getName()); //test singleton_private.id = 3; console.log(singleton_private.getId()); //1 // var tmp = new singleton_private(); }; privateValue();
これなら、idの値を外から書き換えられないですね。
そして、(これが一番正当派?)getInstanceでインスタンスを取得するパターンです(だんだんと複雑になってきますが・・・)
// Use constructor var singleton_constructor = (function(){ var instance; function createInstance(){ var id = 1; var name = "test"; return { getId : function(){ return id; }, getName : function(){ return name; } }; } return { getInstance : function(){ if(!instance){ instance = createInstance(); } return instance; } }; }()); constructor = function(){ console.log("Constructor"); console.log(singleton_constructor.getInstance().getId()); //1 console.log(singleton_constructor.getInstance().getName()); //test singleton_constructor.getInstance().id = 2; console.log(singleton_constructor.getInstance().getId()); //1 console.log(singleton_constructor.id); //undefined }; constructor();
デザインパターンを勉強すると、言語の仕様とか特性とか、勉強になります。