單例模式作為一種常用的設計模式,在 PHP 開發中被廣泛應用。然而,如何設計一個既安全又可復用的單例類,往往是開發者容易忽略的細節。本文將從經典寫法的缺陷出發,引入 PHP 5.3 的後期靜態綁定特性,展示最佳實踐方案。
常見的單例實現通常將構造函數設為 protected,並使用 self 關鍵字引用靜態成員:
class A {
protected static $_instance = null;
protected function __construct() {}
protected function __clone() {}
public function getInstance() {
if (self::$_instance === null) {
self::$_instance = new self();
}
return self::$_instance;
}
}
這種寫法在單一類使用時沒有問題,但當子類繼承父類並未重寫 getInstance 方法時,會發生實例共享的問題:
class B extends A {
protected static $_instance = null;
}
$a = A::getInstance();
$b = B::getInstance();
var_dump($a === $b); // 輸出 bool(true)
根本原因在於 self 在類定義時就被綁定到父類 A,導致子類 B 調用父類方法時,操作的仍是父類的靜態屬性 $_instance。
PHP 5.3 引入的 static 關鍵字(後期靜態綁定)在運行時解析調用類,完美解決此問題:
class C {
protected static $_instance = null;
protected function __construct() {}
protected function __clone() {}
public function getInstance() {
if (static::$_instance === null) {
static::$_instance = new static();
}
return static::$_instance;
}
}
class D extends C {
protected static $_instance = null;
}
$c = C::getInstance();
$d = D::getInstance();
var_dump($c === $d); // 輸出 bool(false)
只需將 self 替換為 static,並確保子類重新聲明 $_instance,即可實現各類獨立的單例實例。
getInstance 方法。掌握後期靜態綁定,讓單例模式在繼承體系中也能安全、優雅地復用。