まめージェント

Android, GAE, AngularJSの技術ネタ中心。Twitter: @mame01112

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();

デザインパターンを勉強すると、言語の仕様とか特性とか、勉強になります。