何番煎じか判りませんがお勉強メモを残します
「HeadFirstデザインパターン」第5章
「Rubyによるデザインパターン」第12章
Singleton パターン
「HeadFirstデザインパターン」でのJavaコードは(だいたい)こんな感じ
- とあるクラスに対してオブジェクトをただ1つだけインスタンス化するための規約
- クラスがインスタンスを1つしか持たないということを保証し、そのインスタンスにアクセスするグローバルポイントを提供する
- クラスにそれ自体が持つ唯一のインスタンスを管理させる
- 他のクラスから勝手に新規インスタンスを作成されないようにする
- インスタンスが必要になった時にはそのクラスに照会するだけで1つのインスタンスが返ってくる
- オブジェクトが1つしか必要ないというケースは数多くある
- スレッドプール・キャッシュ・ダイアログボックス・プリファレンス・レジストリ設定オブジェクト・ロギング用オブジェクト・プリンタやグラフィックカードのデバイスドライバとして働くオブジェクト等 がそれにあたる
- これらのオブジェクトを2つ以上オブジェクト化したなら、多くの場合でプログラムの誤動作・リソースの消費・つじつまの合わない結果といったあらゆる種類の可能性が生じるだろう
(私見)
「これこれこのような場合に、Singletonパターンを使う事により問題が解決する」ようなシンプルな話ではない
「Singletonパターンが必要だ」と思ったのは貴方であり、それを実現する方法は以下になる
public class Singleton {
// 静的変数である
private static Singleton uniqueInstance;
// privateなコンストラクタ これがミソである
// Singletonクラスは外からnewを使用してインスタンス化できないということである
private Singleton() {}
// この静的メソッドはprivateなコンストラクタを使用し
// Singletonクラスをインスタンス化出来る唯一の方法となる
public static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
- これで実装は完了
- だが、マルチスレッドからアクセスされた場合に不都合が起こる可能性がある
- 別のスレッドからほぼ同時に Singleton.getInstance() が呼び出された状況を想定すると、new Singleton() が2度呼び出される状況が容易に考えられる
マルチスレッドSingleton 解決方法1 syncronized
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {}
// synchronizedキーワードを追加する
public static synchronized Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
- ↑の説明です
- getInstance() メソッドに synchronized キーワードが追加されている
- 全てのスレッドはこのメソッドを同時に実行する事は出来ない
- 全てのスレッドはこのメソッドを実行する場合、他のスレッドと歩調を合わせる為に確認し、待機し、確認を続ける。とてもコストが高いものだ
- しかもこの同期化処理は初回のインスタンス生成時以降は全く意味のない処理となる
- 初期化が完了してしまえばオーバーヘッドでしかないということである
マルチスレッドSingleton 解決方法2 まぁそれはそれで
- getInstance() メソッド呼び出しの同期化により100倍程度パフォーマンスが落ちる可能性がある
- が「そこまで頻繁に呼ばれてるわけでもないしなぁ」と貴方が思われる程度なのであれば気にしなくて良いです
- synchronized による同期化はわかりやすく、とても有効だからです
マルチスレッドSingleton 解決方法3 遅延インスタンス作成から先行インスタンス作成に移行する
public class Singleton {
// このクラスがロードされた時点で、インスタンスが作成されている事を保証する
private static Singleton uniqueInstance = new Singleton;
private Singleton() {}
public static synchronized Singleton getInstance() {
return uniqueInstance;
}
}
- ↑の説明です
- コード内のコメントが全てを物語っています
- このインスタンスが最終的に使用されないものだった、としても必ずインスタンスが存在する、してしまう、という事でもある
( ー`дー´;;)
- Singletonパターンとはいったい・・・うごごご!
「Rubyによるデザインパターン」でのRubyコードは(だいたい)こんな感じ
- パターンをあまり知らないプログラマでもsingletonの名前は知っている
- singletonなしではやっていけないし、WEBRick, Rake, ActiveSupport 等そこら中にあふれている
- singletonパターンを生み出した動機は非常に単純で、プログラムから見て、ただ1つしか存在しないものをオブジェクト化するなら、そのオブジェクトはただ1つしか存在しないだろう、と言うこと
- 数多くのコードからそのインスタンスにアクセスする必要がある時、そのオブジェクトを持ち回るよりは、Singletonを作りなさい、そうGofは勧めている
- ただ1つのインスタンスしか持てないクラスで、そのただ1つのインスタンスへのアクセスをグローバルに提供する
とりあえず古典的な方法でSingletonクラスを作ってみる
class SimpleSingleton
# 積極的インスタンス化ですね
@@instance = SimpleSingleton.new
def self.instance
@@instance
end
# ミソ
private_class_method :new
end
SimpleSingleton.new
#=> Traceback (most recent call last):
#=> NoMethodError (private method `new' called for SimpleSingleton:Class)
SimpleSingleton.instance
#=> #<SimpleSingleton:0x00007ff81918a8d0>
SimpleSingleton.instance
#=> #<SimpleSingleton:0x00007ff81918a8d0>
singletonの実装が完了しました。何故なら、Gofのsingletonが求めている要素は全て持っているからです
まぁ、Rubyならこれだけ↓で出来ちゃうんですけどね
- Singletonモジュールはインスタンスを遅延インスタンス化するそうです
require 'singleton'
class SimpleSingleton
include Singleton
end
SimpleSingleton.new
#=> Traceback (most recent call last):
#=> NoMethodError (private method `new' called for SimpleSingleton:Class)
SimpleSingleton.instance
#=> #<SimpleSingleton:0x00007f89d5094320>
SimpleSingleton.instance
#=> #<SimpleSingleton:0x00007f89d5094320>
さて、Singletonの実装方法は以上ですが、このパターンがなぜこれほどまでに疎まれるのか?
- Singletonがグローバル変数によく似ているから
- コードの各所からSingletonを秘密の通信チャネルとして使うことは簡単で、その結果これらは密に結合することになる
- この密結合という恐ろしい結末こそが、プログラマがグローバル変数を敵視する第1の理由です
- Singletonパターンを使うかどうか検討する時は「これがただ1つだけ存在するという事は確かなのだろうか」と自分自身に問いかけ続けてください
- Singletonパターンはオブジェクトに簡単にアクセスできるようにする、という副次的な側面もある
- 本当に唯一存在しているものをオブジェクト化したいのか、「簡単にアクセスできる便利なものが手に入る」という特徴がおまけとして欲しいだけなのか?と
あと(当然過ぎて?)どちらの書籍にも出てきませんでしたが
「唯一のインスタンス」というのは「そのプロセスの中で唯一のインスタンス」という意味ですね