2.2. Understanding dependency injection
理解依赖注入
依赖注入 跟 依赖注入容器 的区别:
- 依赖注入是一种方法 用来编写更好的代码
- 容器是一个工具 帮助注入依赖项
你 不需要 容器也可以进行依赖注入。 但是,容器可以帮助您更好的注入依赖。
PHP-DI 的使命: 让依赖注入更加实用
理论
经典PHP代码
这是 不使用 DI 的代码工作方式:
- 应用需要 Foo (e.g. a controller), 所以:
- 应用创建Foo
- 应用调用Foo
- Foo 需要 Bar (e.g. a service), 所以:
- Foo 创建 Bar
- Foo 调用 Bar
- Bar 需要 Bim (a service, a repository, …), 所以:
- Bar 创建 Bim
- 执行 Bar 的代码
使用依赖注入
这是使用 DI 的代码工作方式:
- 应用需要 Foo, Foo需要Bar、Bim, 所以:
- 应用创建 Bim
- 应用创建 Bar并将Bim给它
- 应用创建 Foo并将Bar给它
- 应用调用 Foo
- Foo调用 Bar
- 执行 Bar 的代码
- Foo调用 Bar
这就是 控制反转模式。从被调用到调用间依赖关系的控制是 倒置的 。
优点:调用链最顶端的总是 你。 您可以控制所有依赖关系,并且可以完全控制应用程序的工作方式。您可以随时用另一个依赖项(例如您创建的依赖项)替换当前依赖。
例如,如果库X使用记录器Y,而您想使其使用记录器Z,该怎么办?使用依赖注入,您不必更改库X的代码。
使用容器
使用PHP-DI的代码如何工作:
- 应用需要Foo,所以:
- 应用从容器中获取Foo,所以:
- 容器创建BIm
- 容器创建Bar给到Bim
- 容器创建Foo给到Bar
- 应用调用Foo
- Foo调用Bar
- Bar执行
- Foo调用Bar
简单来说, 容器帮你省去了所有创建和依赖注入项的工作.
举例说明
这是一个真实示例, 使用经典方式 (使用 new
或者 单例) 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() 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 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自动将哪个GeolocationService注入到StoreService:
$container->set('GeolocationService', \DI\create('GoogleMaps'));
如果您改变主意,现在只需更改一行配置即可。
有兴趣吗?继续阅读 Getting started 指南!
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。