【问题标题】:How to implement a DI container?如何实现一个 DI 容器?
【发布时间】:2014-05-10 16:16:43
【问题描述】:

我一直在问,如果在控制器中创建模型层的实例是错误的,即使它隐藏在这样的工厂后面:

parent::getServiceFactory()->create('Car');

我被告知使用依赖注入容器来初始化我的控制器。我整晚都在谷歌上搜索,昨晚,我了解到依赖注入容器(DiC)用于创建对象的实例,并将所需的对象注入其中,而不是在路由器中手动执行 - 也许我只是误解了概念。

我已阅读此帖子How to build a PHP Dependency Injection Container,它显示了以下代码行:

$ioc->register('database', new DatabaseServiceProvider($host, $user, $pass))

现在,我假设$host, $user, $pass 只是字符串变量,现在如果我想将对象传递给控制器​​,我如何动态知道它需要注入哪些对象?

这样添加类型数组对吗?:

$container->register('SomeController', array('Namespace\to\Model1', 'Namespace\to\Model2'));

然后那个容器寄存器会创建一个SomeController的对象,里面会有Model1,Model2对象

这样做有错吗?在这种情况下使用DI 的正确方法是什么?域对象和数据映射器也是模型层的一部分吗?

或者当应用程序加载时初始化容器中的所有控制器对象是否正确,然后我可以轻松使用任何控制器?

【问题讨论】:

  • 在尝试实际使用它之前,您可能应该先了解什么是 DI 容器以及为什么要使用它。

标签: php oop design-patterns service containers


【解决方案1】:

我被告知使用依赖注入容器来初始化 我的控制器。我整晚都在谷歌上搜索,昨晚,我 了解依赖注入容器(DiC)是用来创建的 一个对象的实例,并将所需的对象注入其中。

没错。

我已阅读这篇文章如何构建 PHP 依赖注入 容器

停在那里。该答案展示了如何构建容器——它展示了如何构建service locator。两者有很大的不同:

  • 服务定位器就像一个键/值存储:您将命名的东西放入其中,然后您按名称请求它们,定位器将它们返回给您。
  • 注射容器就像一个工厂:你要求它生产某种东西,它就会为你生产。 如何创建产品的具体细节可以从完全不透明到完全由您指定。

通俗地说,服务定位器可以做到这一点:

class Perishable
{
    public function __construct(DateTime $expirationDate) { ... }
}

$locator->set('milk', new Perishable(new DateTime());
$milk = $locator->get('milk');

但是,与注射容器相比,它不能这样做:

$milk = $container->build(Perishable::class); // ::class is a PHP 5.5 feature

注入容器能够自行确定Perishable 依赖于DateTime 并解决该依赖关系。这以递归方式发生,并理解所有“叶”依赖项都可以实例化。在这种情况下,DateTime 很容易实例化,因为它是一个具有可以无参数运行的构造函数的具体类;在其他情况下,您必须通过告诉容器如果遇到无法直接实例化的依赖项该怎么做来提供帮助,例如一个Iterator

现在,我假设 $host$user$pass 只是字符串变量,现在如果我 想将对象传递给控制器​​,我怎样才能动态知道哪个 需要注入的对象?

我们现在已经到了实现部分:您如何知道Perishable 需要DateTime,以及如何创建DateTime

您可以使用reflection

$class = new ReflectionClass('Perishable');
$constructor = $class->getConstructor();
$arguments = [];
foreach ($constructor->getParameters() as $parameter) {
    $argumentClass = $parameter->getClass();
    $arguments[] = $argumentClass->newInstance();
}

$result = $class->newInstanceArgs($arguments);

这是可以实例化上面给出的Perishable 的准系统代码,但是在更一般的情况下它当然会失败(还有很多你需要妥善处理的事情,例如递归解析构造函数参数)。

这样添加类型数组对吗?

不,因为您必须违反 DRY:SomeController 的依赖项将作为其构造函数定义的一部分以及在将其注册到容器或定位器时指定。

或者在容器中初始化所有控制器对象是否正确 应用程序加载,然后我可以轻松使用任何控制器?

这根本不对,正如上面所解释的,您使用的是“容器”这个词,而实际上您对服务定位器有一个心理印象。一般来说,服务定位器有许多注入容器没有的缺点。在这种情况下,您应该改用合适的容器。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多