Posted at
LIGincDay 9

知っておきたいデザインパターン 2.シングルトンパターン

More than 3 years have passed since last update.

こんにちは、@Im0_3です。LIGアドベントカレンダーの7日目です!

前回に引き続きデザインパターンについてです。

前回の記事はこちらからご覧ください。

知っておきたいデザインパターン 1.モジュールパターン


シングルトンパターンとは

シングルトンパターンとはとはクラスのインスタンスが1つのオブジェクトに制限されるパターンです。普段AngularJSを扱っているのですが、AngularJSのServiceやFactoryがまさにシングルトンな構造になっています。


実践シングルトンパターン


シングルトンパターン実装例 その1

以下がシングルトンパターンの例です。

var singleton = (function() {

var instance; // [1]
function init(){
var privateValue = 'I\'m Private';
function privateMethod(){
console.log(privateValue);
}
return {
publicMethod: function(){
console.log('I\'m Public');
},
publicPropaty: 'I\'m Public'
}
}
return {
getInstance: function() { // [2]
if(!instance){
instance = init();
}
return instance;
}
};
})();

var singleA = singleton.getInstance();
var singleB = singleton.getInstance();

console.log(singleA === singleB); // true

クロージャーのなかでインスタンスを保持するための変数を持ちます。[1]

そしてsingleton.genInstanceでシングルトンのインスタンスオブジェクトを返します。[2]

最初の実行(singleA)ではインスタンスが生成され、生成されたインスタンスは[1]に保持されます。そして2回目の実行(singleB)ではすでに生成されたオブジェクトを返すことで共通のオブジェクトを保持します。

同じオブジェクトを共有しているため、一方のpublicMethodを変更すると以下のようになります。

singleA.publicMethod = 'change publicMethod!';

console.log(singleB.publicMethod); // change publicMethod!


シングルトンパターン実装例 その2

より簡潔で安全性の高い方法を@frontainerから教えていただきました。

var single = function() {

if (single.$instance) return single.$instance; //[2]
var privateValue = 'I\'m Private';
function privateMethod(){
console.log(privateValue);
}
return single.$instance = {
publicMethod: function(){
console.log('I\'m Public');
},
publicPropaty: 'I\'m Public'
}
};
single.$instance; //[1]

var singleA = single();
var singleB = single();

console.log(singleA === singleB); // true

こちらはsingleに関数を渡して、その関数自身に対して$instanceというプロパティを設定します。[1]

関数が実行された際にはまず、$instance内にインスタンスが存在するかどうかをチェックします。[2] 存在しない際はそれ以降の処理を実行し、$instanceにオブジェクトを代入し返します。存在する場合はそのまま$instanceを返します。

またこの実装方法では以下は全て同じになります。


var singleA = single();
var singleB = single();
var singleC = new single();
var singleD = new single();

console.log(singleA === singleB); // true
console.log(singleC === singleD); // true
console.log(singleA === singleC); // true
console.log(singleB === singleD); // true

このように実装することで、コンストラクタとしてインスタンスを生成しても、関数として実行しても全て同じになります。

ただグローバルからsingle.$instanceからアクセスできてしまうので、を書き換える事ができるので注意が必要です。


シングルトンのメリット・デメリット

シングルトンのメリットは、利用することでインスタンスが一つしか生成されないことが保証されることです。このパターンで書くことによって、いくつかの箇所で生成しても常に同じオブジェクトを共有することができます。例えばWebアプリケーションなどで、データベースにアクセスするは最初の一回に止め、それ以降はデータを使い回したい時などに有効です。

またもともとインスタンスを複数作る設計ではないので、複数インスタンスが存在すると困るようなときに使うのが良いでしょう。

デメリットとしては他のモジュールとの密結合を招き、テストしにくいコードになる恐れがあることです。なので使いどころには注意が必要です。