首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 三大基礎設計模式深度解析:註冊樹、工廠、單例模式的原理與實戰結合

     編輯:PHP 相關知識

PHP 三大基礎設計模式深度解析:註冊樹、工廠、單例模式的原理與實戰結合

設計模式是軟件架構的基石。在 PHP 開發中,**單例、工廠、註冊樹** 是最基礎、最常用、且相互關聯的三大模式。本文從「解決什麼問題」出發,深度解析三者的核心動機、實現細節、組合使用場景,並給出生產級代碼範例。

一、單例模式:確保全局唯一實例

核心動機

  • 資源密集型對象(數據庫連接、Redis 連接、配置管理器)頻繁 new 會耗盡內存/連接數
  • 團隊協作中避免不同開發者各自 new 導致狀態不一致

實現四要素

  1. private/static $instance 靜態保存唯一實例
  2. private/protected __construct() 禁止外部實例化
  3. final protected __clone() 禁止克隆(可選)
  4. 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();

二、工廠模式:封裝對象創建邏輯,解耦調用與實現

核心動機

  • 創建邏輯複雜(依賴注入、配置讀取、多實現選擇)
  • 未來需擴展實現類,不想修改調用處代碼(開閉原則)
  • 統一管理對象生命週期(如連接池、緩存)

簡單工廠 vs 工廠方法

<?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(); // 工廠方法

三、註冊樹模式:全局對象容器,實現服務定位與依賴管理

核心動機

  • 單例/工廠創建的對象分散,缺乏統一管理入口
  • 跨模塊、跨層級調用時,避免層層傳遞依賴(Service Locator 模式)
  • 為其他高級模式(依賴注入容器、AOP、插件系統)提供基礎設施

實現要點

  • 靜態陣列 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
註冊樹全局對象定位跨層調用、舊項目重構、插件掛載服務定位器反模式風險、難追蹤依賴

六、現代 PHP 的最佳實踐建議

  1. 新項目優先使用依賴注入容器(PSR-11):如 PHP-DI、Symfony DI、Laravel Container,編譯時解析依賴圖,類型安全、易測試
  2. 單例僅用於真正的基礎設施:DB、Redis、Config、Logger——業務服務不要做單例
  3. 工廠介面化:定義 FactoryInterface,便於測試時注入 Mock 工廠
  4. 註冊樹僅作為遷移過渡或插件系統:新代碼通過構造函數注入依賴,不主動 Registry::get()

總結:理解三大基礎模式的動機、結構、副作用,是掌握現代 PHP 架構設計的入門鑰匙。實際項目中,**依賴注入容器** 是它們的進化形態,而**單例/工廠/註冊樹** 則是容器內部的構建塊。知其然,更知其所以然,方能在架構演進中游刃有餘。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved