1. 程式人生 > >理解依賴註入

理解依賴註入

關系 injection cat ins 完全 arp 控制反轉 block add

依賴註入依賴註入容器 是不同的:

  • 依賴註入 (Dependency injection) 是編寫更好代碼的一種方法
  • 容器 (Container) 是幫助註入依賴關系的工具

你不需要一個容器來執行依賴註入,但是一個容器可以幫助你。

PHP-DI就是這樣做的:使依賴註入更加實用。

理論

經典的PHP代碼

下面是使用DI的代碼大致工作的方式:

  • 應用程序需要 Foo(例如一個控制器),所以:
  • 應用程序創建 Foo
  • 應用程序調用 Foo
    • Foo 需要 Bar(例如一個服務),所以:
    • Foo 創建 Bar
    • Foo 調用 Bar
      • Bar 需要 Bim(一個服務,一個倉庫……),所以:
      • Bar 創建 Bim
      • Bar 做一些事情

使用依賴註入 (Dependency injection)

下面是使用DI的代碼大致工作的方式:

  • 應用程序需要 Foo ,它需要 Bar,它需要Bim,所以:
  • 應用程序創建 Bim
  • 應用程序創建 Bar 並給它 Bim
  • 應用程序創建 Foo 並給它 Bar
  • 應用程序調用 Foo
    • Foo 調用 Bar
      • Bar 做一些事情

這是控制反轉的模式,被調用者和調用者之間的依賴性控制是相反的

最主要的優點是:在調用鏈頂部的那個總是。你可以控制所有依賴項,並完全控制您的應用程序的工作方式,你可以用另一個(例如你創建的一個)來替換依賴項。

例如,如果庫X使用 Logger Y,而你想讓它使用 Logger Z 呢?有了依賴註入,你就不需要更改庫X的代碼了。

使用容器 (Container)

那麽,使用PHP-DI的代碼是如何工作的:

  • 應用程序需要 Foo,所以:
  • 應用程序從 Container 獲取 Foo,所以:
    • Container 創建 Bim
    • Container 創建 Bar 並給它 Bim
    • Container 創建 Foo 並給它 Bar
  • 應用程序調用 Foo
    • Foo 調用 Bar
      • Bar 做一些事情

簡而言之,容器包含了創建和註入依賴的所有工作

In short, the container takes away all the work of creating and injecting dependencies

.

用一個例子來理解

這是一個真實的例子,比較了一個經典的實現(使用new或單例)和使用依賴註入。

沒有依賴註入

假設你有:

class GoogleMaps
{
    public function getCoordinatesFromAddress($address) {
        // calls Google Maps webservice
    }
}
class OpenStreetMap
{
    public function getCoordinatesFromAddress($address) {
        // calls OpenStreetMap webservice
    }
}
經典的做法是:

class StoreService
{
    public function getStoreCoordinates($store) {
        $geolocationService = new GoogleMaps();
        // or $geolocationService = GoogleMaps::getInstance() if you use singletons

        return $geolocationService->getCoordinatesFromAddress($store->getAddress());
    }
}

現在我們想使用OpenStreetMap而不是GoogleMaps,我們該怎麽做?
我們必須更改StoreService的代碼,以及所有其他使用GoogleMaps的類。

如果沒有依賴註入,你的類與它們的依賴緊耦合。

使用依賴註入

StoreService 現在使用依賴註入:

class StoreService {
    private $geolocationService;

    public function __construct(GeolocationService $geolocationService) {
        $this->geolocationService = $geolocationService;
    }

    public function getStoreCoordinates($store) {
        return $this->geolocationService->getCoordinatesFromAddress($store->getAddress());
    }
}
服務是使用接口 (Interface) 定義的:

interface GeolocationService {
    public function getCoordinatesFromAddress($address);
}

class GoogleMaps implements GeolocationService { ...

class OpenStreetMap implements GeolocationService { ...
現在,StoreService的用戶可以決定使用哪個實現。 它可以隨時改變,不必重寫StoreService。

StoreService不再與它的依賴緊耦合。

The StoreService is no longer tightly coupled to its dependency.

使用 PHP-DI
你可能會發現依賴註入會帶來一個缺點:你現在必須處理註入依賴關系。

這就是容器(Container),特別是PHP-DI可以幫助你的地方。

而不是寫:
$geolocationService = new GoogleMaps();
$storeService = new StoreService($geolocationService);
你可以寫:

$storeService = $container->get(‘StoreService‘);

  並配置哪個GeolocationService PHP-DI應該通過配置自動註入到StoreService中:

$container->set(‘GeolocationService‘, \DI\create(‘GoogleMaps‘));

  

如果您改變主意,現在只需要改變一行配置。

感興趣嗎? 繼續閱讀開始使用PHP-DI指南!

參考

  • 理解依賴註入
  • PHP-單例模式和工廠模式

p.s. 看到PHP-DI沒有中文文檔,第一次對著機翻瞎翻譯,如有疏漏敬請指正。

理解依賴註入