🐺

PHP デザインパターン学習:Singleton パターンの基礎

に公開

本記事のサンプルコードの完全版は GitHub リポジトリで公開しています

Singleton(シングルトン)パターンとは

Singleton パターンは、あるクラスのインスタンスが必ず 1 つだけ存在することを保証するデザインパターンです。グローバルな状態や設定の管理、共有リソースへのアクセス制御などに使用されます。

Singleton パターンの主な特徴

  • クラスのインスタンスが常に 1 つだけ存在することを保証
  • グローバルなアクセスポイントを提供
  • 複数のインスタンス生成を防止する仕組み

PHP での Singleton パターンの実装方法

Singleton パターンを実装するには以下の要素が必要です:

  1. private コンストラクタ: 外部からのインスタンス化を禁止
  2. private クローンメソッド: 複製を禁止
  3. public wakeup メソッド(例外をスロー): シリアライズ/デシリアライズからの復元を禁止
  4. 静的インスタンス変数: 唯一のインスタンスを保持
  5. 静的ゲッターメソッド: インスタンスにアクセスするための公開メソッド

実装例: Logger クラス

ログ管理は Singleton パターンの典型的な適用例です。アプリケーション内のどこからでも同じログインスタンスにアクセスできるようにすることで、ログの一貫性が保たれます。

以下は Logger クラスの核となる部分です(完全なコードはこちらを参照):

class Logger
{
    // 唯一のインスタンスを保持する静的プロパティ
    private static $instance = null;

    // コンストラクタをprivateにして外部からのインスタンス化を防止
    private function __construct() {
        $this->logFile = __DIR__ . '/application.log';
    }

    // cloneを禁止
    private function __clone() {}

    // unserializeを禁止(PHP 8ではpublicが必要)
    public function __wakeup() {
        throw new Exception("Cannot unserialize a singleton.");
    }

    // インスタンスを取得するメソッド
    public static function getInstance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    // ログメソッドの例
    public function log($message, $level = 'INFO') {
        $date = date('Y-m-d H:i:s');
        $logMessage = "[$date] [$level] $message" . PHP_EOL;
        file_put_contents($this->logFile, $logMessage, FILE_APPEND);
        return true;
    }
}

Logger クラスの使用例

実際の使用方法は以下のように非常にシンプルです:

サンプル

// Loggerのインスタンスを取得
$logger = Logger::getInstance();

// ログを記録
$logger->log("アプリケーションが起動しました", "INFO");

// 別の場所でも同じインスタンスを利用
$anotherLogger = Logger::getInstance();

// 同じインスタンスかどうか確認
if ($logger === $anotherLogger) {
    echo "同じインスタンスです!\n";
} else {
    echo "異なるインスタンスです!\n";
}

Singleton パターンのメリット

  1. グローバルアクセス: アプリケーションのどこからでも同じインスタンスにアクセス可能
  2. 一貫性: 設定や状態が一貫して維持される
  3. リソース効率: リソースを共有することで効率的に使用できる
  4. 遅延初期化: 必要になるまでインスタンスが生成されない

Singleton パターンのデメリット

  1. グローバル状態: テストの難しさやバグの原因になる可能性
  2. 単一責任の原則に違反: インスタンス管理と本来の機能が混在
  3. 依存関係が不明確: 依存性注入に比べて明示的でない
  4. 並行処理の問題: マルチスレッド環境では注意が必要

PHP における注意点

  • PHP 8 では、マジックメソッド(__wakeup など)は public 宣言が必要
  • セッション間でのシングルトンの維持はできない(PHP はリクエストごとに終了する)
  • サブクラス化には注意が必要(静的変数と self の扱い)

まとめ

Singleton パターンは、特定のクラスのインスタンスが 1 つだけ存在することを保証するシンプルで強力なデザインパターンです。適切な場面で使用すれば、コードの効率性と一貫性を高めることができます。

今回実装した Logger クラスのように、アプリケーション全体で一貫したログ記録が必要な場合は、Singleton パターンが適しています。ただし、グローバル状態の導入によるデメリットも考慮し、必要性を十分に検討した上で使用することが重要です。

Discussion