[PHP-DI] 理解依賴注入

理解依賴注入

依賴注入依賴注入容器 是不一樣的:php

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

你不須要一個容器來執行依賴注入,可是一個容器能夠幫助你。html

PHP-DI就是這樣作的:使依賴注入更加實用。git

理論

經典的PHP代碼

下面是使用DI的代碼大體工做的方式:github

  • 應用程序須要 Foo(例如一個控制器),因此:
  • 應用程序建立 Foo
  • 應用程序調用 Foo
    • Foo 須要 Bar(例如一個服務),因此:
    • Foo 建立 Bar
    • Foo 調用 Bar
      • Bar 須要 Bim(一個服務,一個倉庫……),因此:
      • Bar 建立 Bim
      • Bar 作一些事情

使用依賴注入 (Dependency injection)

下面是使用DI的代碼大體工做的方式:web

  • 應用程序須要 Foo ,它須要 Bar,它須要Bim,因此:
  • 應用程序建立 Bim
  • 應用程序建立 Bar 並給它 Bim
  • 應用程序建立 Foo 並給它 Bar
  • 應用程序調用 Foo
    • Foo 調用 Bar
      • Bar 作一些事情

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

最主要的優勢是:在調用鏈頂部的那個老是。你能夠控制全部依賴項,並徹底控制您的應用程序的工做方式,你能夠用另外一個(例如你建立的一個)來替換依賴項。this

例如,若是庫X使用 Logger Y,而你想讓它使用 Logger Z 呢?有了依賴注入,你就不須要更改庫X的代碼了。翻譯

使用容器 (Container)

那麼,使用PHP-DI的代碼是如何工做的:code

  • 應用程序須要 Foo,因此:
  • 應用程序從 Container 獲取 Foo,因此:
    • Container 建立 Bim
    • Container 建立 Bar 並給它 Bim
    • Container 建立 Foo 並給它 Bar
  • 應用程序調用 Foo
    • Foo 調用 Bar
      • Bar 作一些事情

簡而言之,容器包含了建立和注入依賴的全部工做htm

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指南!

參考

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

相關文章
相關標籤/搜索