2016-01-06 125 views
0

我正在編寫一個相當大的應用程序,並已開始研究不同的設計模式以編寫「更好的代碼」。我已經開始使用PHPUnit,並發現如果沒有依賴注入,我的代碼將不容易測試,因爲如果沒有它,我將無法注入模擬類。我很喜歡依賴注入的想法。管理全局依賴關係

我對問題的理解更多的是與項目中依賴關係的管理有關。具體來說,我會創建全局的,比如數據庫連接。所以現在,我不必成爲一個全局變量,而必須將它作爲依賴注入 - 這很好。

但是,如果我在數據庫對象的實例化和實際需要的地方之間有大量的函數調用,這就是它開始讓我感到困惑的地方。對我來說,每個函數都會依賴數據庫對象是沒有意義的。對我來說,將它放在具有所有全局依賴項的依賴容器中並且讓我的代碼中的每個函數都具有對容器的依賴關係也沒有任何意義。

想象一下嵌套的函數調用,如下所示。

<?php 

    function foo() 
    { 
     bar(); 
    } 

    function bar() 
    { 
     // Now we need to inject an instance of Database, but we don't have it! 
     baz(); 
    } 

    function baz(Database $database) 
    { 
     // work with the database here 
    } 

    $database = new Database(); 
    foo(); 

我可以初始化我的數據庫,我甚至可以把它放在一個容器中。數據庫類只能創建一次,並且對應用程序來說是全局的。然而,使用上面的代碼,如果baz執行一些數據庫操作,現在我必須將數據庫作爲實例傳遞給foo和bar。如果我把數據庫放在一個容器中,那麼我就通過容器。然而,foo和bar可能不需要關心baz是否與數據庫一起工作。由於Database類的性質,每次需要使用它時,實例化數據庫的設計都會很差。

我想過如何解決這個問題,我的想法之一是使用static :: getInstance方法,但是我在DI上閱讀的越多,我越喜歡這種方法,我只是覺得它是錯了,而且我正在用錯誤的方式去解決這個問題。

我可以看到的另一個選擇是擁有一個純粹靜態的get/set方法的容器類和一個包含數據的靜態私有數組。然而,我在那裏實現的是封裝全球範圍。

我應該如何管理我的全局依賴關係,並通過從實例化到注入的鏈?

回答

1

在這裏您將獲得與用戶一樣多的意見;) 我們在我們公司使用以下方法。

我們有一個全球工廠類。這個類負責管理依賴關係。顯然,它很大,因爲它處理所有可能對象的實例化,但很容易測試。 好的是,這個工廠的大多數方法都是私有的。因爲你只需要最上面的對象。其他一切都是隱藏的。

這是一個數據庫的例子。您想收集來自數據庫的所有產品。

class ProductCollector 
{ 
    private $repository; 

    public function __construct(ProductRepository $repository) 
    { 
     $this->repository = $repository; 
    } 

    public function collect() 
    { 
     $collection = $this->repository->getAllProducts(); 
     // Do something with collection 
     // ... 
     return $collection; 
    } 
} 

class ProductRepository 
{ 
    private $pdo; 

    public function __construct(\PDO $pdo) 
    { 
     $this->pdo = $pdo; 
    } 

    public function getAllProducts() 
    { 
     //... Here you can use PDO 
    } 
} 

class Factory 
{ 
    public function createProductCollector() 
    { 
     return new ProductCollector($this->createProductRepository()); 
    } 

    private function createProductRepository() 
    { 
     return new ProductRepository($this-createDbConnection()); 
    } 

    private function createDbConnection() 
    { 
     // For the sake of this example we don't care how you get the DB credentials into the factory. 
     return new \PDO('mysql:dbname=testdb;host=127.0.0.1', 'username', 'password'); 
    } 
} 

現在在你的代碼,你可以做這樣的事情:

$factory = new Factory(); 

$collector = $factory->createProductCollector(); 
$collection = $collector->collect(); 

這是當然的,只是許多可能的解決方案,以應對DI之一。