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

PHP 依賴注入(DI)與控制反轉(IoC)深度解析:從手動注入到容器化管理

     編輯:PHP 綜合知識

為什麼需要依賴注入?

傳統寫法在類內部 new 依賴,導致高耦合、難測試、難替換。依賴注入(DI)將「建立依賴」的職責外移,由呼叫端或容器提供,實現鬆耦合。

演進歷程:五個階段

階段 1:硬編碼依賴(反面教材)

class SomeComponent {
    public function someDbTask() {
        $conn = new Connection(['host'=>'localhost', ...]); // 死綁定
    }
}

❌ 無法替換資料庫類型、參數、測試用 Mock

階段 2:Setter 注入(手動)

class SomeComponent {
    protected $conn;
    public function setConnection(Connection $conn) { $this->conn = $conn; }
}
$comp = new SomeComponent();
$comp->setConnection(new Connection($config));

✅ 可外部替換,但每次使用都要手動組裝,依賴多時極其繁瑣

階段 3:登記表模式

class Registry {
    public static function getSharedConnection() {
        static $conn = null;
        return $conn ??= new Connection($config);
    }
}
$comp->setConnection(Registry::getSharedConnection());

✅ 解決單例共享,但引入全域狀態,隱式依賴難追蹤

階段 4:工廠模式

class SomeComponent {
    public static function factory() {
        return new self(
            new Connection(), new Session(), new FileSystem(), ...
        );
    }
}

✅ 封裝建立邏輯,但工廠本身成為「上帝類」,違反單一職責

階段 5:DI 容器(最佳實踐)

class SomeComponent {
    protected $di;
    public function __construct($di) { $this->di = $di; }
    public function someDbTask() {
        $conn = $this->di->get('db');           // 每次新實例
        $conn = $this->di->getShared('db');     // 共享單例
    }
}

$di = new Phalcon\DI();
$di->set('db', fn() => new Connection($config));
$di->set('filter', fn() => new Filter());
$comp = new SomeComponent($di);

DI 容器核心優勢

特性說明
延遲載入只有真正呼叫 get() 時才實例化,節省資源
單例管理getShared() 確保同一請求內複用實例
參數覆蓋呼叫時動態傳參:$di->get('comp', [$param1, $param2])
多種註冊方式類名、閉包、實例、陣列定義,靈活適應場景
解耦測試測試時輕鬆替換為 Mock 物件

Phalcon\DI 註冊方式對比

// 1. 類名(簡單、無參數)
$di->set('request', 'Phalcon\Http\Request');

// 2. 閉包(延遲載入、可注入參數、最推薦)
$di->set('db', function() {
    return new Phalcon\Db\Adapter\Pdo\Mysql([
        'host' => 'localhost', 'username' => 'root',
        'password' => 'secret', 'dbname' => 'blog'
    ]);
});

// 3. 直接實例(立即建立、無延遲)
$di->set('request', new Phalcon\Http\Request());

// 4. 陣列定義(可後續修改參數)
$di->set('db', [
    'className' => 'Phalcon\Db\Adapter\Pdo\Mysql',
    'parameters' => ['host' => 'localhost', ...]
]);
// 後續可動態修改:
$di->setParameter('db', 0, ['host' => 'newhost']);

// 獲取服務
$request = $di->get('request');           // 新實例
$request = $di->getShared('request');     // 共享單例
$request = $di->getRequest();             // 魔術方法別名

IoC 容器 vs 服務定位器

⚠️ 反模式警示:在類中直接呼叫 $di->get('service') 屬於「服務定位器」模式,隱藏了真實依賴。
✅ 最佳實踐:建構子注入宣告依賴,容器自動解析注入(如 Laravel、Symfony 現代框架)。

總結

依賴注入不只是「傳參數」,而是一套管理物件生命週期、解耦業務邏輯與基礎設施、支援測試與擴展的架構模式。從手動注入到 DI 容器,是從「手工組裝」進化到「自動化裝配線」的必經之路。

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