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

PHP 設計模式:註冊模式實作全域物件存取點

     編輯:PHP 綜合知識

註冊模式:物件的「電話簿」——集中管理、全域存取

註冊模式 提供一個集中儲存、查找物件實例的機制,避免在層層函數間傳遞依賴 (Tramp Data)。常與單例模式結合,成為應用程式的「服務定位器」。

核心實作 (PHP 4/5 相容)

<?php
class Registry {
    private static ?self $instance = null;
    private array $store = [];
    
    // 單例模式:防止直接實例化
    private function __construct() {}
    private function __clone() {}
    
    public static function getInstance(): self {
        return self::$instance ??= new self();
    }
    
    // 核心操作
    public function set(string $key, mixed $value): void {
        $this->store[$key] = $value;
    }
    
    public function get(string $key): mixed {
        return $this->store[$key] ?? null;
    }
    
    public function has(string $key): bool {
        return isset($this->store[$key]);
    }
    
    public function remove(string $key): void {
        unset($this->store[$key]);
    }
    
    public function clear(): void {
        $this->store = [];
    }
    
    // 批次操作
    public function setMultiple(array $values): void {
        foreach ($values as $key => $value) {
            $this->set($key, $value);
        }
    }
    
    public function getMultiple(array $keys): array {
        return array_intersect_key($this->store, array_flip($keys));
    }
}
?>

使用範例:資料庫連線、配置、服務註冊

<?php
// 啟動時註冊核心服務
$registry = Registry::getInstance();

// 1. 資料庫連線 (單例)
$registry->set('db', new PDO('mysql:host=localhost;dbname=app', 'user', 'pass'));

// 2. 配置物件
$registry->set('config', [
    'app_name' => 'MyApp',
    'debug' => true,
    'cache_ttl' => 3600,
]);

// 3. 快取服務
$registry->set('cache', new RedisCache(new Redis()));

// 4. 日誌服務
$registry->set('logger', new FileLogger('/var/log/app.log'));

// 任意處存取
function getUser(int $id): ?User {
    $db = Registry::getInstance()->get('db');
    $stmt = $db->prepare("SELECT * FROM users WHERE id = ?");
    $stmt->execute([$id]);
    return $stmt->fetchObject(User::class) ?: null;
}

// 控制器中使用
$logger = Registry::getInstance()->get('logger');
$logger->info('User accessed: ' . $_SERVER['REQUEST_URI']);
?>

進階:型別安全註冊器 (PHP 7.4+)

<?php
class TypedRegistry {
    private array $store = [];
    
    /** @var array */
    private array $typeMap = [];
    
    public function register(string $key, mixed $value, string $expectedType): void {
        if (!$value instanceof $expectedType && gettype($value) !== $expectedType) {
            throw new TypeError("Value for '$key' must be $expectedType, " . gettype($value) . " given");
        }
        $this->store[$key] = $value;
        $this->typeMap[$key] = $expectedType;
    }
    
    /** @template T
     * @param class-string $expectedType
     * @return T|null
     */
    public function get(string $key, string $expectedType): mixed {
        if (!isset($this->store[$key])) return null;
        
        $value = $this->store[$key];
        if (!$value instanceof $expectedType && gettype($value) !== $expectedType) {
            throw new TypeError("Stored value for '$key' is not $expectedType");
        }
        return $value;
    }
    
    public function has(string $key): bool {
        return isset($this->store[$key]);
    }
}

// 使用
$registry = new TypedRegistry();
$registry->register('db', new PDO(...), PDO::class);
$registry->register('config', ['debug'=>true], 'array');

$db = $registry->get('db', PDO::class); // IDE 自動完成、靜態分析通過
$config = $registry->get('config', 'array');
?>

註冊模式 vs 依賴注入 (DI) 容器

特性 註冊模式 DI 容器
物件建立 手動建立並註冊 容器自動建立 (反射/定義)
依賴解析 手動 get() 自動遞迴注入
生命週期管理 手控 (單例/原型) 內建 (singleton, prototype, request...)
型別安全 需自行實作 現代容器支援 (PHP-DI, Symfony DI)
測試友善 易於替換 (set mock) 支援覆寫、Mock 定義
複雜度 低 (幾十行代碼) 高 (需學習容器配置)
適用專案 中小型、舊專案遷移 中大型、新專案、微服務

測試中的註冊模式應用

<?php
// 測試前置
class TestCase extends PHPUnit\Framework\TestCase {
    protected function setUp(): void {
        // 重置註冊器
        $registry = Registry::getInstance();
        $registry->clear();
        
        // 註冊 Mock 服務
        $mockDb = $this->createMock(PDO::class);
        $registry->set('db', $mockDb);
        
        $mockLogger = $this->createMock(LoggerInterface::class);
        $registry->set('logger', $mockLogger);
    }
}

// 整合測試
public function testUserRegistration() {
    $registry = Registry::getInstance();
    $registry->set('email_service', new MockEmailService());
    
    $controller = new UserController();
    $controller->register('test@example.com', 'password');
    
    // 驗證...
}
?>

反模式警示:濫用註冊模式

⚠️ 何時不應使用註冊模式

  • ❌ 取代正常的參數傳遞 (函數簽名應明確依賴)
  • ❌ 臨時變數、請求級資料存放 (用 Request 物件)
  • ❌ 作為全域變數垃圾桶 (導致隱性耦合、難以追蹤)
  • ❌ 多執行緒/協程環境無鎖定機制 (PHP-FPM 通常無此問題)

✅ 適合註冊的物件

  • 資料庫連線、快取客戶端、日誌器、配置物件
  • 核心服務:郵件發送器、檔案存儲、API 客戶端
  • 單例服務、昂貴建構的共享資源
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved