[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没有中文文档,第一次对着机翻瞎翻译,若有疏漏敬请指正。

相关文章
相关标签/搜索