設計模式是軟件架構的基石。在 PHP 開發中,**單例、工廠、註冊樹** 是最基礎、最常用、且相互關聯的三大模式。本文從「解決什麼問題」出發,深度解析三者的核心動機、實現細節、組合使用場景,並給出生產級代碼範例。
new 會耗盡內存/連接數new 導致狀態不一致private/static $instance 靜態保存唯一實例private/protected __construct() 禁止外部實例化final protected __clone() 禁止克隆(可選)public static getInstance() 統一獲取入口,延遲初始化<?php
final class Database {
private static ?self $instance = null;
private PDO $pdo;
private function __construct() {
$this->pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
public static function getInstance(): self {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function getPdo(): PDO { return $this->pdo; }
// 禁止克隆與反序列化
private function __clone() {}
public function __wakeup() { throw new Exception("Cannot unserialize singleton"); }
}
// 使用
$pdo = Database::getInstance()->getPdo();
<?php
// 1. 簡單工廠:靜態方法根據參數返回不同實現
interface Cache { public function get(string $key); public function set(string $key, $val, int $ttl); }
class RedisCache implements Cache { /* ... */ }
class FileCache implements Cache { /* ... */ }
class CacheFactory {
public static function create(string $type = 'redis'): Cache {
return match($type) {
'redis' => new RedisCache(),
'file' => new FileCache(),
default => throw new InvalidArgumentException("Unknown cache type: $type"),
};
}
}
// 2. 工廠方法:定義介面,子類決定實例化哪個類(更符合開閉原則)
interface CacheFactoryInterface { public function create(): Cache; }
class RedisCacheFactory implements CacheFactoryInterface { public function create(): Cache { return new RedisCache(); } }
class FileCacheFactory implements CacheFactoryInterface { public function create(): Cache { return new FileCache(); } }
// 使用
$cache = CacheFactory::create('redis'); // 簡單工廠
$factory = new RedisCacheFactory(); $cache = $factory->create(); // 工廠方法
self::$objects = [] 存儲別名→實例映射set($alias, $object)、get($alias)、unset($alias) 三大核心操作set 可接受 Closure,get 時執行並緩存<?php
class Registry {
private static array $objects = [];
private static array $factories = [];
public static function set(string $alias, object|Closure $object): void {
if ($object instanceof Closure) {
self::$factories[$alias] = $object;
unset(self::$objects[$alias]);
} else {
self::$objects[$alias] = $object;
unset(self::$factories[$alias]);
}
}
public static function get(string $alias): object|null {
if (isset(self::$objects[$alias])) return self::$objects[$alias];
if (isset(self::$factories[$alias])) {
$obj = (self::$factories[$alias])();
self::$objects[$alias] = $obj;
unset(self::$factories[$alias]);
return $obj;
}
return null;
}
public static function has(string $alias): bool {
return isset(self::$objects[$alias]) || isset(self::$factories[$alias]);
}
public static function remove(string $alias): void {
unset(self::$objects[$alias], self::$factories[$alias]);
}
}
// 註冊單例/工廠產出的對象
Registry::set('db', Database::getInstance());
Registry::set('cache', fn() => CacheFactory::create('redis')); // 懶加載
// 任意處獲取
$db = Registry::get('db');
$cache = Registry::get('cache');
<?php
// 1. 單例提供基礎設施
class Config {
private static ?self $instance = null;
private array $data = [];
private function __construct() { $this->data = require 'config/app.php'; }
public static function getInstance(): self { return self::$instance ??= new self(); }
public function get(string $key, $default = null) { return $this->data[$key] ?? $default; }
}
// 2. 工廠根據配置創建具體實現
class StorageFactory {
public static function create(): FilesystemAdapter {
$driver = Config::getInstance()->get('storage.driver', 'local');
return match($driver) {
's3' => new S3Adapter(Config::getInstance()->get('storage.s3')),
'local' => new LocalAdapter(Config::getInstance()->get('storage.local')),
default => throw new Exception("Unknown storage driver: $driver"),
};
}
}
// 3. 註冊樹統一掛載,業務代碼零依賴具體類
Registry::set('config', Config::getInstance());
Registry::set('storage', fn() => StorageFactory::create());
// 4. 業務層使用
class UserService {
public function uploadAvatar(int $uid, UploadedFile $file): string {
$storage = Registry::get('storage');
return $storage->put("avatars/$uid", $file->getContent());
}
}
| 模式 | 核心職責 | 適用場景 | 注意事項 |
|---|---|---|---|
| 單例 | 全局唯一實例 | DB連接、配置、日誌、緩存客戶端 | 難測試、隱式依賴、併發需加鎖 |
| 工廠 | 封裝創建邏輯 | 多實現切換、複雜構造、插件系統 | 避免過度設計、簡單場景直接 new |
| 註冊樹 | 全局對象定位 | 跨層調用、舊項目重構、插件掛載 | 服務定位器反模式風險、難追蹤依賴 |
FactoryInterface,便於測試時注入 Mock 工廠Registry::get()總結:理解三大基礎模式的動機、結構、副作用,是掌握現代 PHP 架構設計的入門鑰匙。實際項目中,**依賴注入容器** 是它們的進化形態,而**單例/工廠/註冊樹** 則是容器內部的構建塊。知其然,更知其所以然,方能在架構演進中游刃有餘。