Singleton
一言でいうと
Singleton (シングルトン) は、 生成に関するデザインパターンの一つで、 クラスが一つのインスタンスのみを持つことを保証するとともに、 このインスタンスへの大域アクセス・ポイントを提供します。
問題
Singleton パターンは、 単一責任の原則に違反しますが、 二つの問題を同時に解決します:
-
クラスのインスタンスが一つだけであるであることを保証します。 いったい誰がクラスのインスタンス数を管理したいかですって? 最も一般的な理由は、 データベースやファイルなどの共有資源へのアクセスを制御するためです。
こう動作します: オブジェクトを作ったとしましょう。 しばらくしてもう一つ新しいものを作ることにしました。 できたてのオブジェクトを受け取る代わりに、 すでに作成したオブジェクトを取得することになります。
この振る舞いは、 通常のコンストラクターでは実現不能であることに注意してください。 コンストラクターの呼び出しは、 設計上、 常に新規のオブジェクトを返します。
-
そのインスタンスへの大域アクセス・ポイントを提供する。 昔、 重要なオブジェクトを保存するためにあなた (はい、 実は私です) が使用していたグローバル変数、 覚えていますか? 非常に便利なのですが、 どんなコードでもそれらの変数の内容を変更でき、 アプリがクラッシュする能性があるため、 非常に危険です。
Singleton パターンはグローバル変数と同じように、 プログラム内のどこからでもオブジェクトのアクセスを許します。 しかし、 そのインスタンスが他のコードによって変更されるのを防止することもします。
この問題には、 もう一つの側面があります。 問題点その 1 を解くコードをプログラム全体にまき散らかすのを避けるということです。 特に、 あなたのコードの残りの部分がすでにそのクラスに依存している場合、 一つのクラス内にまとめておく方がはるかに良いです。
現在 Singleton パターンは非常に普及しているため、 上記の問題点の一つだけを解決する何らかの技法のことが、 シングルトンと呼ばれることもあります。
解決策
Singleton のすべての実装に共通した、 二つのステップがあります:
- 他のオブジェクトがシングルトンのクラスの
new
演算子を使用しないように、 デフォルトのコンストラクターを非公開 (private) にします。 - コンストラクターとして機能する静的 (static) 作成メソッドを作成します。 内部では、 このメソッドは、 非公開のコンストラクターを呼び出してオブジェクトを生成し、 静的なフィールドに保存します。 次回以降のこのメソッドへの呼び出しはすべて、 キャッシュされたオブジェクトを返します。
コードがこのシングルトン・クラスにアクセスできれば、 シングルトンの静的メソッドを呼び出すことができます。 そのため、 そのメソッドが呼び出されるたびに、 常に同じオブジェクトが返されます。
現実世界でのたとえ
政府はシングルトンの優れた例です。 一つの国には、 一つの公式な政府しかありません。 政府を構成する人々がどうであれ、 「X 国政府」 は、 政権を掌握した人々の集団を指す、 グローバルなアクセス・ポイントとなります。
構造
-
シングルトン (Singleton) クラスは、 そのクラスの同じインスタンスを返す静的メソッド
getInstance
を宣言します。シングルトンのコンストラクターは、 クライアント・コードから隠れている必要があります。
getInstance
メソッドの呼び出しが、 シングルトンのオブジェクトを取得する唯一の方法でなければなりません。
擬似コード
この例では、 データベース接続クラスがシングルトンとして機能します。 このクラスには、 公開コンストラクターがありません。 このクラスのオブジェクトを取得する唯一の方法が、 getInstance
メソッドを呼び出すことにするためです。 このメソッドは、 最初に作成されたオブジェクトをキャッシュし、 その後の呼び出しでは、 それを返します。
適応性
プログラム内のあるクラスがすべてのクライアントで利用可能な単一のインスタンスを持つ必要がある場合に、 Singleton パターンを使用します。 たとえば、 プログラムの異なる部分で共有されている単一のデータベース・オブジェクトです。
Singleton パターンは、 特別な作成メソッドを除いて、 クラスのオブジェクトを作成する他のすべての方法を無効にします。 このメソッドは、 新しいオブジェクトを作成するか、 すでに作成されている場合は既存のオブジェクトを返します。
グローバル変数をより厳密に管理する必要がある場合は、 Singleton パターンを使用してください。
グローバル変数とは異なり、 Singleton パターンはクラスのインスタンスが一つだけであることを保証します。 Singleton クラス自体を除くいかなるものも、 キャッシュされたインスタンスを置き換えることはできません。
この制限を調整して、 任意個のシングルトンのインスタンスを作成するようにすることは、 いつでも可能です。 変更が必要なコードは、 getInstance
メソッドの本体だけです。
実装方法
-
クラスにシングルトンのインスタンスを格納する非公開 (private) の静的 (static) フィールドを追加します。
-
シングルトンのインスタンスを取得するための公開 (public) 静的 (static) 作成用メソッドを宣言します。
-
静的メソッド内に、 遅延初期化コードを実装します。 初回の呼び出し時だけ新規オブジェクトを作成し、 それを静的フィールドに格納します。 その後の呼び出しでは、 メソッドは常にそのインスタンスを返すようにします。
-
コンストラクターを非公開 (private) とします。 クラス中の静的メソッドからはこのコンストラクターを呼べますが、 他のオブジェクトからは呼べません。
-
クライアントのコード中のコンストラクター呼び出しのすべてを静的生成用メソッドの呼び出しと置き換えます。
長所と短所
- クラスにはインスタンスが一つしかないことが保証可能。
- そのインスタンスへの大域アクセス・ポイントが得られる。
- シングルトンのオブジェクトは、 初回要求時にのみ初期化。
- 単一責任の原則に違反。 このパターンは、 二つの問題点を同時に解決しようとするため。
- Singleton パターンは、 設計上の欠陥を隠蔽。 たとえば、 プログラム中のコンポーネント同士が互いの詳細を知りすぎるなど。
- このパターンの使用にあたっては、 マルチスレッド環境において、 複数のスレッドがシングルトン・オブジェクトを複数回生成しないように特別な処理が必要。
- 多くのテスト・フレームワークがモック・オブジェクトの生成において継承に依存しているため、 シングルトンのクライアント・コードは、 ユニット・テストが困難。 シングルトンのクラスのコンストラクターは非公開のため、 ほとんどの言語で静的メソッドを上書きすることが不可能。 シングルトンのモックを行うには、 巧妙な方法を考える必要あり。 またはテストを放棄。 または Singleton パターンの使用をあきらめる。
他のパターンとの関係
-
多くの場合、 ファサード・オブジェクトは一つだけあれば十分なので、 Facade はしばしば Singleton に変換可能です。
-
Flyweight で、 共有状態の全部を一つのフライウェイト・オブジェクトに何らかの方法で削減できた場合、 それは Singleton に似たものになります。 しかし、 この二つのパターンには、 根本的な違いが二箇所あります。
- Singleton のインスタンスは一つだけですが、 Flyweight クラスは、 異なる内因的状態を持つ複数のインスタンスがある可能性があります。
- Singleton オブジェクトは変更可能かもしれませんが、 Flyweight のオブジェクトは不変です。
-
Abstract Factories、 Builders、 Prototypes はどれも Singletons で実装可能です。