Singleton パターン
Singleton パターンは、クラスのインスタンスが確実に1つだけ存在することを保証し、そのインスタンスにグローバルにアクセスする方法を提供するデザインパターンです。システム全体で共有される設定情報やリソース管理、データベース接続などに使用されます。
目的と用途
Singleton パターンの主な目的は以下の通りです:
- クラスのインスタンスが1つだけであることを保証する
- そのインスタンスへのグローバルなアクセスポイントを提供する
- 初回アクセス時に遅延初期化(Lazy Initialization)を行う
一般的な用途:
- 設定情報の管理
- データベース接続プールの管理
- ファイルシステムやネットワークリソースへのアクセス
- ロギングサービス
- キャッシュの実装
クラス図
図1: Singleton パターンのクラス図
シーケンス図
図2: Singleton パターンのシーケンス図
実装例
- 基本実装
- 遅延初期化
- スレッドセーフ
- Enumによる実装
最もシンプルな Singleton の実装です。静的フィールドとプライベートコンストラクタを使用します。
public class Singleton { // 唯一のインスタンスを静的フィールドとして保持 private static final Singleton instance = new Singleton(); // プライベートコンストラクタでインスタンス化を制限 private Singleton() { // 初期化処理 } // インスタンスへのアクセスポイントを提供 public static Singleton getInstance() { return instance; } // ビジネスロジック public void someBusinessMethod() { // 何らかの処理 System.out.println("ビジネスロジックの実行"); } }
遅延初期化(Lazy Initialization)を使用した実装です。初回のアクセス時にのみインスタンスを生成します。
public class LazySingleton { // 唯一のインスタンスを保持する変数(初期値はnull) private static LazySingleton instance; // プライベートコンストラクタ private LazySingleton() { // 初期化処理 } // 遅延初期化を行うgetInstanceメソッド public static LazySingleton getInstance() { if (instance == null) { instance = new LazySingleton(); } return instance; } // ビジネスロジック public void someBusinessMethod() { System.out.println("ビジネスロジックの実行"); } }
スレッドセーフな実装です。マルチスレッド環境でも1つのインスタンスのみが生成されることを保証します。
public class ThreadSafeSingleton { // volatile修飾子を使用して可視性を保証 private static volatile ThreadSafeSingleton instance; private ThreadSafeSingleton() { // 初期化処理 } // Double-Checked Lockingパターンを使用 public static ThreadSafeSingleton getInstance() { // 最初のチェック(ロックなし) if (instance == null) { // クラスに対する同期ブロック synchronized (ThreadSafeSingleton.class) { // 二重チェック if (instance == null) { instance = new ThreadSafeSingleton(); } } } return instance; } public void someBusinessMethod() { System.out.println("ビジネスロジックの実行"); } }
Java 5以降で推奨されるEnum型を使用した実装です。シリアライズの問題も解決します。
public enum EnumSingleton { INSTANCE; // Enumの唯一のインスタンス // 初期化ブロック EnumSingleton() { // 初期化処理 } public void someBusinessMethod() { System.out.println("ビジネスロジックの実行"); } } // 使用例 public class Main { public static void main(String[] args) { EnumSingleton singleton = EnumSingleton.INSTANCE; singleton.someBusinessMethod(); } }
メリットとデメリット
メリット
- クラスのインスタンスが1つだけであることを保証
- グローバルなアクセスポイントを提供
- 初期化の遅延実行が可能
- リソースの共有と再利用が可能
デメリット
- 単体テストが難しくなる場合がある
- グローバル状態を導入するため、依存関係が隠れる
- マルチスレッド環境では実装が複雑になる
- 継承による拡張が制限される
使用上の注意点
- 並行処理: マルチスレッド環境では、同期化に注意が必要です。
- シリアライズ: Java でシリアライズを使用する場合、readResolve() メソッドを実装するか、Enum 型を使用する必要があります。
- クラスローダー: 複数のクラスローダーが使われる環境では、複数のインスタンスが生成される可能性があります。
- 依存関係の注入: 依存関係の注入(DI)を使用しているシステムでは、DI コンテナと協調する必要があります。
関連パターン
- Factory Method: Singleton のインスタンスを生成するために Factory Method パターンを使用することがあります。
- Abstract Factory: Singleton として実装されることが多いパターンです。
- Builder: 複雑なオブジェクトの構築を担当する Builder も Singleton として実装されることがあります。
- Facade: サブシステムへのインターフェースを提供する Facade も Singleton として実装されることがあります。