【译文】PHP-DI:理解依赖注入(Understanding Dependency Injection)
文章来源于PHP-DI,作者:Matthieu Napoli。原文地址(英文):http://php-di.org/doc/understanding-di.html。由于译者水平有限以及翻译中存在语义损失,建议阅读原文。
PHP-DI是用PHP编写的、强大的和实用的、框架无关的依赖注入容器。PHP-DI在本站【实用开源目录】链接:http://www.worldlink.com.cn/osdir/pos/developer/ap...
依赖注入和依赖注入容器是不同的东西:
- 依赖注入是一种方法,用于编写更好的代码
- 容器是一种工具,用以帮助注入依赖性
你不需要一个容器来做依赖注入。 但是一个容器可以帮助你。
PHP-DI就是这样:使依赖注入更加实用。
#原理
#经典PHP代码
如果不使用DI、一个代码大致是如何工作的呢:
- 应用程序需要Foo(例如控制器),因此:
- 应用程序创建Foo
- 应用程序调用Foo
- Foo需要Bar(例如服务),因此:
- Foo创建Bar
- Foo调用Bar
- Bar需要Bim(一个服务,一个仓库,...),因此:
- Bar创建Bim
- Bar做某事
#使用依赖注入
以下是使用DI的代码的大致工作流程:
- 应用程序需要Foo,Foo需要Bar,Bar需要Bim,因此:
- 应用程序创建Bim
- 应用程序创建Bar并给它Bim
- 应用程序创建Foo并给它Bar
- 应用程序调用Foo
- Foo调用Bar
- Bar做某事
- Foo调用Bar
这是控制反转的模式。依赖性的控制从一个被调用到一个调用被反转。
主要优点:在调用链顶部的始终是你。您可以控制所有依赖项,并完全控制应用程序的工作方式。你可以用另一个(一个你做的例子)替换依赖。
例如,如果Library X使用Logger Y,而你想让它使用你的logger Z?使用依赖注入,您不必更改Library X的代码。
#使用容器
现在使用PHP-DI的代码是如何工作的呢:
- 应用程序需要Foo,因此:
- 应用程序从容器获取Foo,因此:
- 容器创建Bim
- 容器创建Bar并给它Bim
- 容器创建Foo并给它Bar
- 应用程序调用Foo
- Foo调用Bar
- Bar做某事
- Foo调用Bar
简而言之,容器带走了创建和注入依赖项的所有工作。
#用一个例子来理解
这是一个现实生活中的例子,比较一个经典的实现(使用 new 或 singletons)VS 使用依赖注入。
#没有依赖注入
比如说你有:
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() 如果你使用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 GeolocationService { public function getCoordinatesFromAddress($address); } class GoogleMaps implements GeolocationService { ... class OpenStreetMap implements GeolocationService { ...
现在,StoreService的用户决定使用哪个实现。 它可以随时更改,而无需重写StoreService。StoreService不再紧密耦合到它的依赖。
#用PHP-DI
您可能会看到依赖注入将留下一个缺点:您现在必须处理注入依赖项。
这正是一个容器---特别是PHP-DI可以帮到你的地方。
代替如下写法:
$geolocationService = new GoogleMaps(); $storeService = new StoreService($geolocationService);
你可以这样写:
$storeService = $container->get('StoreService');
并设定通过配置、PHP-DI在StoreService中应自动注入哪一个GeolocationService:
$container->set('GeolocationService', \DI\object('GoogleMaps'));
现在如果你改变主意,只需改变一行配置。
感兴趣吗?继续阅读PHP-DI入门指南 !