Singletonパターン
CoddyのPHPジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 71/91。
シングルトンパターンは、アプリケーションのライフサイクル全体を通じて、クラスのインスタンスが1つだけであることを保証する生成に関するデザインパターンです。また、そのインスタンスへのグローバルなアクセスポイントも提供します。
このパターンは、データベース接続、設定マネージャー、ロガーなど、システム全体のアクションを調整するために、正確に1つのオブジェクトが必要な場合に役立ちます。これらのインスタンスを複数作成すると、リソースの無駄になったり、一貫性のない動作の原因になったりします。
PHPでシングルトンを実装する方法は次のとおりです:
<?php
class Database {
private static ?Database $instance = null;
private function __construct() {
// プライベートなコンストラクタにより、直接のインスタンス化を防ぎます
}
public static function getInstance(): Database {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function query(string $sql): string {
return "Executing: $sql";
}
}
$db1 = Database::getInstance();
$db2 = Database::getInstance();
echo $db1 === $db2 ? "Same instance" : "Different instances";
出力:
Same instanceこのパターンは、3つの主要な要素に基づいています:newを直接使用することを防ぐプライベートコンストラクタ、単一のインスタンスを保持する静的プロパティ、そして、最初の呼び出し時にインスタンスを作成し、それ以降の呼び出しではそのインスタンスを返す静的メソッドです。
シングルトンを真に堅牢なものにするには、クローン作成とアンシリアライズも防ぐ必要があります:
<?php
private function __clone() {}
public function __wakeup() {
throw new Exception("Cannot unserialize singleton");
}
チャレンジ
簡単シングルトンパターンを使用して、設定マネージャーを構築しましょう。実際のアプリケーションでは、通常、設定情報の信頼できる唯一の情報源(Single Source of Truth)が必要になります。複数の設定オブジェクトが存在すると、システム全体で動作の不整合が生じる可能性があるためです。
コードは2つのファイルに分けて構成します:
Config.php— シングルトンパターンを実装したConfigクラスを作成します。設定マネージャーは以下の要件を満たす必要があります:- 設定を private な配列プロパティに保存する
- 設定配列を空として初期化する private なコンストラクタを持つ
- 唯一のインスタンスを返す static な
getInstance()メソッドを提供する __clone()を private にすることでクローンを防止する- 設定を保存するための
set(string $key, string $value): voidメソッドを含める - キーに対応する値を返す
get(string $key): ?stringメソッドを含める(キーが存在しない場合はnullを返す) - すべての設定を返す
getAll(): arrayメソッドを含める
main.php— Config ファイルを読み込みます。入力としてキーと値の2つを受け取ります。Config インスタンスを取得し、キー
"app_name"、値"MyApp"でデフォルト設定をセットしてください。(再度
getInstance()を使用して)Config インスタンスへの2つ目の参照を取得し、それを使用して入力されたキーと値で別の設定をセットしてください。最後に、シングルトンが正しく動作しているかを確認します:
- 両方の参照が同じオブジェクトを指しているかどうかに基づいて、
"Same instance: true"または"Same instance: false"と出力します(比較には===を使用してください) - 保存されている設定の総数を示す
"Settings count: [count]"を出力します(getAll()の結果に対してcount()を使用してください) - 2つ目の参照を通じて取得した app_name 設定を示す
"App name: [value]"を出力します
- 両方の参照が同じオブジェクトを指しているかどうかに基づいて、
このチャレンジは、シングルトンパターンの核心的な利点を示しています。getInstance() を何度呼び出しても、常に同じオブジェクトが取得されるため、アプリケーションのすべての部分で同じ設定状態を共有できることが保証されます。
チートシート
Singleton パターンは、アプリケーションのライフサイクル全体を通じてクラスのインスタンスが1つだけであることを保証し、そのインスタンスへのグローバルなアクセスポイントを提供します。
このパターンは、データベース接続、設定マネージャー、ロガーなど、複数のインスタンスが存在するとリソースの無駄遣いになったり、動作の不整合を引き起こしたりする可能性があるオブジェクトに役立ちます。
基本的な Singleton の実装:
<?php
class Database {
private static ?Database $instance = null;
private function __construct() {
// Private constructor prevents direct instantiation
}
public static function getInstance(): Database {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
}
$db1 = Database::getInstance();
$db2 = Database::getInstance();
echo $db1 === $db2 ? "Same instance" : "Different instances";
// Output: Same instance
主要な要素:
- プライベートコンストラクタ —
newを直接使用することを防ぎます - 静的プロパティ — 単一のインスタンスを保持します
- 静的メソッド — 初回呼び出し時にインスタンスを作成し、それ以降の呼び出しではそのインスタンスを返します
クローンとアンシリアライズの防止:
<?php
private function __clone() {}
public function __wakeup() {
throw new Exception("Cannot unserialize singleton");
}
自分で試してみよう
<?php
require_once 'Config.php';
// 入力を読み込む
$key = trim(fgets(STDIN));
$value = trim(fgets(STDIN));
// TODO: Config インスタンスを取得し、"app_name" を "MyApp" に設定する
// TODO: getInstance() を再度使用して、Config への2つ目の参照を取得する
// TODO: 2つ目の参照を使用して、入力された $key と $value を設定する
// TODO: Singleton が動作しているか確認する:
// - === 比較に基づいて "Same instance: true" または "Same instance: false" を出力する
// - 設定の総数を示す "Settings count: [count]" を出力する
// - 2つ目の参照を通じて取得した app_name を示す "App name: [value]" を出力する
?>このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。