什么是依赖注入容器?

杰特罗·黑泽尔赫斯特(Jethro Hazelhurst)

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

据我了解,DIC就像标题中所暗示的那样:一个将所有依赖项收集在一起的容器。无需查看new Foo\Bar整个应用程序,而是在容器内部生成所有新实例,然后在需要它们的地方互相传递(例如,Model用的实例实例Database化,用的实例实例化Config)。

我试图做一个非常简单的DIC。这就是结果。

在前端控制器中,我正在实例化一个新对象App\Core\Container

我的Container样子是这样的:

<?php

namespace App\Core;

use App\Config;

class Container
{
    public $config;
    public $router;
    public $database;

    public $model;
    public $view;
    public $controller;

    public function __construct()
    {
        $this->config   = new Config;
        $this->router   = new Router;
        $this->database = new Database($this->config);
    }

    public function add()
    {
        // add dependencies from the outside?
    }

    public function getInstance(/* string */)
    {
        // return an instance for use somewhere?
    }

    public function newModel($model)
    {
        $model = $this->getModelNamespace() . $model;
        $this->model = new $model($this->database);
        return $this->model;
    }

    private function getModelNamespace()
    {
        $namespace = 'App\Models\\';
        if (array_key_exists('namespace', $this->params = [])) {
            $namespace .= $this->params['namespace'] . '\\';
        }
        return $namespace;
    }

    public function newView($params)
    {
        $this->view = new View($this->model, $params);
        return $this->view;
    }

    public function newController($controller)
    {
        $controller = $this->getControllerNamespace() . $controller;
        $this->controller = new $controller;
        return $this->controller;
    }

    private function getControllerNamespace()
    {
        $namespace = 'App\Controllers\\';
        if (array_key_exists('namespace', $this->params = [])) {
            $namespace .= $this->params['namespace'] . '\\';
        }
        return $namespace;
    }
}

问题

  • 我上面的实现虽然很简单,但是可以归类为基本的依赖注入程序吗?
  • 依赖注入容器通常由一类组成吗?
杰里米

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

可以将其归类为依赖项注入容器吗?

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

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

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

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

这取决于。您可以很容易地用一个类制作一个简单的依赖项注入容器。问题在于,简单容器的性质趋向于发展成不太简单的容器。当您开始改善模式时,您需要考虑不同组件如何一起发挥作用。问问自己:他们是否遵循SOLID原则?如果不是,则必须进行重构。

什么是依赖项注入容器?

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

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

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

依赖项注入容器将检测依赖项。通常,这是通过以下3种机制之一进行的:自动装配,注释和定义。PHP-DI文档提供什么所有这三个继承权是一个好主意在这里简而言之,尽管:自动装配通过反映来检测依赖关系,注解用于使用类上方的注释来编写依赖关系,而定义则用于对依赖关系进行硬编码。就个人而言,我更喜欢自动布线,因为它既干净又简单。

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

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

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

<?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()方法构建一个类并依赖于先前共享的类时,它都将使用已经实例化的实例来代替实例化一个新实例。

对于一个简单而功能强大的依赖项注入容器,我建议使用Auryn,但无论如何,请尝试理解并创建自己的依赖,然后再使用现有的那些。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章