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 として実装されることがあります。