理解依賴註入
阿新 • • 發佈:2019-04-10
關系 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 做一些事情
- Foo 調用 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 做一些事情
- Foo 調用 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沒有中文文檔,第一次對著機翻瞎翻譯,如有疏漏敬請指正。
理解依賴註入