本文介绍了什么是依赖注射容器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



据了解,DIC就像标题表明:将所有依赖关系集中在一起的容器。所有新的实例都不是在应用程序中看到 new Foo\Bar ,而是在容器内部生成,然后在需要的地方传递给彼此(例如, 模型被实例化为一个数据库的实例,实例是 Config )。



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



在我的前台控制器中,我正在实例化一个新的 App \Core\Container



我的容器如下所示:

 <?php 

命名空间App \Core;

使用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()
{
//从外部添加依赖项?
}

public function getInstance(/ * string * /)
{
//返回一个用于某处的实例?
}

public function newModel($ model)
{
$ model = $ this-> getModelNamespace()。 $模型;
$ this-> model = new $ model($ this->数据库);
返回$ this->模型;
}

私有函数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()。 $控制器;
$ this-> controller = new $ controller;
return $ this-> controller;
}

私有函数getControllerNamespace()
{
$ namespace ='App \Controllers\\\'';
if(array_key_exists('namespace',$ this-> params = [])){
$ namespace。= $ this-> params ['namespace']。 \\;
}
return $ namespace;
}
}

问题




  • 我上面的实现虽然很简单,但可以被归类为基本依赖注入器吗?

  • 是依赖关系注射容器通常由一个类组成?


解决方案



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



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

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



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



一个依赖注入容器是否包含一个类?



我可以把它留在依赖。显然,这不是建设性的,所以我会尝试和阐述。你可以很容易地用一个类创建一个简单的依赖注入容器。问题是简单的容器的性质往往会更加先进到一个不太简单的容器。当您开始改进模式时,您需要考虑不同组件的组合方式。问问自己:他们遵循SOLID原则吗?



什么是依赖注入容器?



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



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



依赖注入容器如何查找依赖关系?



依赖注入容器将检测依赖关系。通常情况下,这通过3种机制中的一种进行:自动连线,注释和定义。 PHP-DI文档提供了一个好主意,其中三个都包含。简而言之,虽然:自动装配通过在类上检测到依赖关系,但使用注释使用类上方的注释写入依赖关系,并使用定义来硬编码依赖关系。就个人而言,我喜欢自动装配,因为它是干净的



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



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



使用自动装配的简单注射器的一个简单例子就像这个:

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

//创建class-to-make的构造函数的反射以获取依赖关系
$ classReflection = new ReflectionMethod($ className,__construct);

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

//使用注入器创建依赖关系的实例
$ dependencies [] = $ this-> make($ dependencyName);
}

$ class = new ReflectionClass($ className);

//实例化所有依赖关系的类
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(){echoWorld! }
}

$ injector = new注射器;

$ a = $ inject-> make(A);
//无需手动实例化A的依赖关系,B或B的依赖关系,C

$ a-> output();

此基本注射器有明显的故障。例如,如果两个类别相互依赖,则有机会创建递归灾难(应该有一个检查)。但是,同样的,这可以作为注射器的一个基本示例。



注射器与依赖注入容器



为了使这个更强大,属于依赖注入容器的定义,您需要一种在多个 make()呼叫。例如,您可能还有另一种方法叫做 share()。此方法将存储传递给它的实例。每当一个类是通过 make()方法构建的,并且依赖于之前共享的类,而不是实例化一个新实例,它将使用已经实例化的类。 p>

对于简单&强大的依赖注入容器我建议,但一定要尝试了解&在使用已经可用的那些之前创建自己的。


I am trying to understand the role of a Dependency Injection Container because it strikes me as fundamental in maintainable code.

As I understand it, a DIC is just as the title suggests: a container where all your dependencies are collected together. Instead of seeing new Foo\Bar all over the application, all the new instances are generated inside of the container and then passed through to each other where they are needed (e.g., Model is instantiated with an instance of Database, which is instantiated with an Instance of Config).

I have attempted to make a very simple DIC. This is the result.

In my front controller I am instantiating a new App\Core\Container.

My Container looks like this:

<?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;
    }
}

Questions

  • Could my implementation above, although very simple, be classed as a basic dependency injector?
  • Is a Dependency Injection Container generally comprised of one class?

解决方案

Could this be classified as a dependency injection container?

No, this does not look like a dependency injection container. A dependency injection container is meant to reduce the work that instantiation requires by determining, creating, and injecting all dependencies. Rather what you have there appears to be a combination of a factory and a service locator.

Factories abstract the creation of objects. This is essentially what your Container class is doing. By calling designated methods (i.e., newModel), your container takes on the responsibility of locating the exact object to be instantiated and constructing an instance of that object.

The reason I would call this a "poor" factory is that it's beginning to look like it might be used to locate services. Service locators work by hiding an object's dependencies: instead of being dependent on GenericService, an object might depend on a service locator. Given the service locator, it can request an instance of GenericService. I see similar behavior beginning to take hold in your add() and getInstance() methods. Service locators are generally considered anti-patterns because they abstract dependencies therefore making code impossible to test!

Is a dependency injection container comprised of one class?

I could leave it at "it depends." Obviously, that's not constructive, so I'll try and elaborate. You could very easily make a simple dependency injection container with one class. The issue is that the nature of a simple container tends to get more advanced into a not-so-simple container. When you start improving your pattern, you need to consider how the different components play together. Ask yourself: do they follow SOLID principles? If not, refactoring is necessary.

What is a dependency injection container?

I said it above, but again: a dependency injection container is meant to reduce the work that instantiation requires by determining, creating, and injecting all dependencies. A DIC will look at all dependencies of a class, and all dependencies those dependencies may have, and so on... In this sense, the container is responsible for hierarchically instantiating all dependencies.

The Container class you provide relies on very strict definitions of pre-defined classes. For example, classes in your model layer appear to only be dependent on a database connection. (Similar statements can be said about classes in your controller & view layer).

How does a dependency injection container find dependencies?

A dependency injection container will detect dependencies. Typically this happens through 1 of 3 mechanisms: autowiring, annotations, and definitions. PHP-DI docs provide a good idea of what all three of these entail here. In short, though: autowiring detects dependencies by reflecting on a class, annotations are used to write in dependencies using comments above a class, and definitions are used to hard-code dependencies. Personally, I prefer autowiring because it's clean & simple.

Can I create a simple dependency injection container or no?

Yes, you can. Start with the idea that an injector should be able to instantiate any object (service, view, controller, etc...). It needs to look at the relevant object and hierarchically instantiate all dependencies (hint: possibly through some method of recursion).

A quick example of a simple injector using autowiring looks like this:

<?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);
    }
}

Tested with something like the following, you can see how the injector recursively checks and instantiates all 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();

This basic injector has obvious faults. For example, there is an opportunity to create a recursion disaster if two classes are dependent on each other (there should be a check for that). However, as is, this works as a basic example of what an injector looks like.

Injector vs. Dependency Injection Container

To make this more powerful and fall under the definition of "dependency injection container", you'd want a way to share instantiated instances across multiple make() calls. For example, you may have another method called share(). This method would store the instance passed to it. Whenever a class is built through the make() method and depends on a class previously shared, instead of instantiating a new instance, it would use the already-instantiated one.

For a simple & powerful dependency injection container I suggest Auryn, but by all means, try to understand & create your own before using the ones already available.

这篇关于什么是依赖注射容器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-02 01:46