如何实现一个DI容器?

17 浏览
0 Comments

如何实现一个DI容器?

我一直在打听,在控制器中创建模型层的实例是否有问题,即使它被隐藏在一个类似这样的工厂后面: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的正确方法是什么?领域对象和数据映射器是否属于模型层的一部分?或者在应用程序加载时将所有控制器对象初始化到容器中,然后我可以轻松地使用任何控制器?

0
0 Comments

如何实现一个DI容器?

在这篇文章中,作者被告知要使用一个依赖注入容器来初始化他的控制器。他在网上搜索了整晚,昨晚,他了解到依赖注入容器(DiC)是用来创建对象实例并将所需的对象注入其中的。

这是正确的。

他读了一篇文章《如何构建一个PHP依赖注入容器》。

停下来。那个答案并没有展示如何构建一个容器 - 它展示了如何构建一个服务定位器。这两者之间有很大的区别:

- 服务定位器就像一个键/值存储:你把命名的东西放进去,以后你按照名字来询问它,定位器会把它们返回给你。

- 一个注入容器就像一个工厂:你请求它生产某个东西,它为你生产出来。产品的具体创建方式可以从完全不透明到完全可由你指定。

简单来说,一个服务定位器可以做到这一点:

class Perishable
{
    public function __construct(DateTime $expirationDate) { ... }
}
$locator->set('milk', new Perishable(new DateTime());
$milk = $locator->get('milk');

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

$milk = $container->build(Perishable::class); // ::class是PHP 5.5的一个特性

一个注入容器能够自行确定Perishable依赖于一个DateTime,并解析该依赖关系。这个过程是递归进行的,它知道所有的“叶子”依赖关系都可以被实例化。在这种情况下,DateTime很容易实例化,因为它是一个具体类,具有可以无参数运行的构造函数;在其他情况下,您需要告诉容器如何处理遇到无法直接实例化的依赖关系,例如一个Iterator。

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

我们现在进入了实现部分:您如何知道Perishable需要一个DateTime,以及如何创建这个DateTime?

您可以使用反射来做到这一点:

$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的依赖关系既在其构造函数定义中指定,又在将其注册到容器或定位器时指定。

或者,在应用程序加载时初始化所有控制器对象,然后我可以轻松地使用任何控制器?

这完全不对,正如上面解释的一样,在这里你使用的是“容器”这个词,但实际上你脑海中的形象是一个服务定位器。一般来说,服务定位器有很多注入容器没有的缺点。在这种情况下,你应该使用一个适当的容器。

0