【译文】PHP-DI:理解依赖注入(Understanding Dependency Injection)

你不需要一个容器来做依赖注入。 但是一个容器可以帮助你。PHP-DI就是这样:使依赖注入更加实用。

文章来源于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做某事

这是控制反转的模式。依赖性的控制从一个被调用到一个调用被反转。

主要优点:在调用链顶部的始终是你。您可以控制所有依赖项,并完全控制应用程序的工作方式。你可以用另一个(一个你做的例子)替换依赖。

例如,如果Library X使用Logger Y,而你想让它使用你的logger Z?使用依赖注入,您不必更改Library X的代码。

#使用容器

现在使用PHP-DI的代码是如何工作的呢:

  • 应用程序需要Foo,因此:
  • 应用程序从容器获取Foo,因此:
    • 容器创建Bim
    • 容器创建Bar并给它Bim
    • 容器创建Foo并给它Bar
  • 应用程序调用Foo
    • Foo调用Bar
      • 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入门指南


To the top