什么是依赖注入容器?

时间:2016-08-01 15:29:02

标签: php oop dependency-injection inversion-of-control

我试图了解依赖注入容器的作用,因为它使我在可维护代码中成为基础。

据我了解,DIC正如标题所暗示的那样:一个容器,其中所有依赖项都被收集在一起。不是在整个应用程序中看到new Foo\Bar,而是在容器内部生成所有新实例,然后在需要它们的地方相互传递(例如,Model使用{{的实例进行实例化。 1}},使用Database的实例进行实例化。

我试图制作非常简单的 DIC。这就是结果。

在我的前端控制器中,我正在实例化一个新的Config

我的App\Core\Container看起来像这样:

Container

问题

  • 上面的实现虽然非常简单,但可以归类为基本的依赖注入器吗?
  • 依赖注入容器通常由一个类组成吗?

1 个答案:

答案 0 :(得分:7)

  

注意:前三个标题会回答您的问题,而以下标题会回答预期的问题并提供前两个部分的任何内容。

这可以归类为依赖注入容器吗?

不,这看起来不像依赖注入容器。依赖注入容器意味着通过确定,创建和注入所有依赖项来减少实例化所需的工作。而是您拥有的工具似乎是工厂和服务定位器的组合。

工厂抽象对象的创建。这基本上就是你的Container班级正在做的事情。通过调用指定的方法(即newModel),您的容器负责定位要实例化的确切对象并构造该对象的实例。

我称之为“贫穷”工厂的原因是它开始看起来可能用于定位服务。 服务定位器通过隐藏对象的依赖关系来工作:对象可能依赖于服务定位器,而不是依赖于GenericService。给定服务定位器,它可以请求GenericService的实例。我看到类似的行为开始在您的add()getInstance()方法中占据一席之地。服务定位器通常被认为是反模式,因为它们抽象依赖性因此使代码无法测试!

依赖注入容器是否由一个类组成?

取决于。你可以很容易地用一个类创建一个简单的依赖注入容器。问题是简单容器的性质往往会更加先进到不那么简单的容器中。当您开始改进模式时,您需要考虑不同组件如何一起使用。问问自己:他们是否遵循SOLID原则?如果没有,重构是必要的。

什么是依赖注入容器?

我在上面说过,但是:依赖注入容器意味着通过确定,创建和注入所有依赖项来减少实例化所需的工作。 DIC将查看类的所有依赖关系,以及这些依赖关系可能具有的所有依赖关系等等......在这个意义上,容器负责分层实例化所有依赖关系

您提供的Container类依赖于对预定义类的非常严格的定义。例如,模型层中的类似乎依赖于数据库连接。 (关于控制器和视图层中的类,可以说类似的陈述。)

依赖注入容器如何找到依赖项?

依赖注入容器将检测依赖项。通常,这通过3种机制中的1种发生:自动装配,注释和定义。 PHP-DI文档很好地理解了这三者所需的内容here。简而言之,尽管:自动装配通过reflecting检测类的依赖关系,但注释用于使用类上方的注释编写依赖关系,并且定义用于硬编码依赖关系。就个人而言,我更喜欢自动装配,因为它很干净。简单。

我可以创建一个简单的依赖注入容器吗?

是的,你可以。首先考虑注入器应该能够实例化任何对象(服务,视图,控制器等......)。它需要查看相关对象并分层实例化所有依赖项(提示:可能通过某种递归方法)。

使用自动装配的简单注射器的快速示例如下所示:

<?php
class Injector
{
    public function make($className)
    {
        $dependencies = [];

        //Create reflection of the class-to-make's constructor to get dependencies
        $classReflection = new ReflectionMethod($className, "__construct");

        foreach($classReflection->getParameters() as $parameter) {
            $dependencyName = $parameter->getClass()->getName();

            //Use the injector to make an instance of the dependency
            $dependencies[] = $this->make($dependencyName);
        }

        $class = new ReflectionClass($className);

        //Instantiate the class with all dependencies
        return $class->newInstanceArgs($dependencies);
    }
}

使用以下内容进行测试,您可以看到注入器如何递归检查并实例化所有依赖项

class A {
    protected $b;
    public function __construct(B $b) { $this->b = $b; }
    public function output(){ $this->b->foo(); }
}

class B {
    protected $c;
    public function __construct(C $c) { $this->c = $c; }
    public function foo() { $this->c->bar(); }
}

class C {
    public function __construct() { }
    public function bar() { echo "World!"; }
}

$injector = new Injector;

$a = $injector->make("A");
//No need to manually instantiate A's dependency, B, or B's dependency, C

$a->output();

这种基本喷射器有明显的故障。例如,如果两个类彼此依赖(应该检查它),则有机会创建递归灾难。但是,按原样,这可以作为注入器外观的基本示例。

注射器与依赖注射容器

为了使其更强大并且属于“依赖注入容器”的定义,您需要一种方法来跨多个make()调用共享实例化实例。例如,您可能有另一种名为share()的方法。此方法将存储传递给它的实例。每当通过make()方法构建类并依赖于先前共享的类,而不是实例化新实例时,它将使用已经实例化的类。

简单&amp;强大的依赖注入容器我建议Auryn,但无论如何,请尝试理解&amp;在使用已有的之前创建自己的。