当前位置: 首页 > 面试题库 >

了解IoC容器和依赖注入

温智明
2023-03-14
问题内容

我的理解:

  • 依赖关系是当ClassA实例需要ClassB实例实例化ClassA的新实例时。
  • 依赖项注入是通过ClassA的构造函数中的参数或通过set〜DependencyNameHere〜(〜DependencyNameHere〜$ param)函数将ClassA传递给ClassB的实例时进行的。 (这是我不确定的领域之一)
  • IoC容器是单例类(在任何给定时间只能实例化1个实例),可以在该容器中实例化该项目的那些类的对象的特定方法。这是指向我正在尝试描述的示例以及我一直在使用的IoC容器的类定义的示例的链接

因此,在这一点上,我开始尝试将IoC容器用于更复杂的场景。到目前为止,为了使用IoC容器,似乎我要创建的几乎所有要在IoC容器中定义依赖项的类都具有has-
a关系。如果我想创建一个继承类的类怎么办,但仅当父类已以特定方式创建且已在IoC容器中注册时,该怎么办。

因此,例如:我想创建mysqli的子类,但是我想在IoC容器中注册该类,以便仅以以前以我在IoC容器中注册的方式构造的父类实例化。我想不出一种无需复制代码的方法(并且由于这是一个学习项目,我试图将其保持为“纯”)。这是我要描述的更多示例。

所以这是我的一些问题:

  • 在不违反OOP某些原则的前提下,我正在尝试做的事情是否可能?我知道在c ++中我可以使用动态内存和复制构造函数来完成此操作,但是我无法在php中找到这种功能。(我承认,除了__construct以外,我几乎没有使用其他魔术方法的经验,但是如果我理解正确的话,从阅读和__clone来说,我无法在构造函数中使用它来使子类实例化为父类的实例)。
  • 我的所有依赖关系类定义都应在哪里与IoC有关?(我的IoC.php顶部是否应该有一堆require_once(’dependencyClassDefinition.php’)?我的直觉是有更好的方法,但是我还没有想出办法)
  • 我应该在哪个文件中注册对象?当前在类定义之后对IoC.php文件中的IoC :: register()进行所有调用。
  • 在注册需要该依赖关系的类之前,是否需要在IoC中注册依赖关系?由于在实际实例化在IoC中注册的对象之前,我不会调用匿名函数,所以我猜不是,但这仍然是一个问题。
  • 我还有其他需要做的事情吗?我试图一次将其迈出一步,但是我也不想知道我的代码将可重用,最重要的是,一个对我的项目一无所知的人可以阅读并理解它。

问题答案:

简而言之(因为这不仅仅限于OOP世界), 依赖 就是组件A需要(依赖)组件B来完成应做的工作的情况。该词还用于描述这种情况下的依赖组件。为了用OOP
/ PHP术语来表示,请考虑以下示例,与强制性汽车类比类似:

class Car {

    public function start() {
        $engine = new Engine();
        $engine->vroom();
    }

}

Car 依赖EngineEngineCar依赖 。但是这段代码非常糟糕,因为:

  • 依赖关系是隐式的;在检查Car的代码之前,您不知道它的存在
  • 班级紧密相连;你不能代替EngineMockEngine用于测试目的或TurboEngine扩展原有的一个,而无需修改Car
  • 汽车能够自己制造引擎看起来有点愚蠢,不是吗?

依赖注入 是一种方法,它通过使Car需要的事实变得Engine明确并为它提供一个条件来解决所有这些问题:

class Car {

    protected $engine;

    public function __construct(Engine $engine) {
        $this->engine = $engine;
    }

    public function start() {
        $this->engine->vroom();
    }

}

$engine = new SuperDuperTurboEnginePlus(); // a subclass of Engine
$car = new Car($engine);

上面是 构造函数注入
的示例,其中通过类构造函数将依赖项(被依赖对象)提供给依赖项(消费者)。另一种setEngine方法是在Car类中公开一个方法,并使用该方法注入的实例Engine。这称为“
setter注入” ,主要用于应该在运行时交换的依赖项。

任何不平凡的项目都由一堆相互依赖的组件组成,并且很容易很快就就无法确定注入了什么。一个 依赖注入容器
是一个知道如何实例化和配置其他对象的对象,知道他们与项目的其他对象的关系是并执行依赖注入你。这使您可以集中管理所有项目的(相互)依赖关系,更重要的是,可以更改/模拟一个或多个依赖关系,而不必在代码中编辑很多地方。

让我们抛开汽车类比,以OP试图实现的示例为例。假设我们有一个Database取决于mysqli对象的对象。假设我们要使用一个真正原始的依赖项指示符容器类DIC,该类公开两种方法:register($name, $callback)以给定名称注册创建对象的方法,并resolve($name)从该名称获取对象。我们的容器设置如下所示:

$dic = new DIC();
$dic->register('mysqli', function() {
    return new mysqli('somehost','username','password');
});
$dic->register('database', function() use($dic) {
    return new Database($dic->resolve('mysqli'));
});

请注意,我们告诉容器mysqli 从其自身
获取一个实例来组装的实例Database。然后,要获得一个Database实例,它的依赖项会自动注入,我们将简单地:

$database = $dic->resolve('database');

这就是要旨。Pimple是一个稍微复杂但仍相对简单易懂的PHP DI /
IoC容器。查看其文档以获取更多示例。

关于OP的代码和问题:

  • 不要对容器使用静态类或单例(或与此有关的任何其他东西);他们都是邪恶的。请改用Pimple。
  • 确定是要 扩展mysqliWrapper类还是要 依赖 它。 mysql
  • 通过IoC从内部调用,mysqliWrapper您可以将一个依赖关系交换为另一个依赖关系。您的对象不应该知道或使用容器;否则,它不再是DIC,而是服务定位器(反)模式。
  • require在将文件注册到容器中之前,不需要类文件,因为您根本不知道是否要使用该类的对象。将所有容器设置放在一个地方。如果您不使用自动加载器,则可以require在向容器注册的匿名函数内部。


 类似资料:
  • 容器和依赖注入 5.1版本正式引入了容器的概念,用来更方便的管理类依赖及运行依赖注入。 5.0版本已经支持依赖注入的,依赖注入和容器没有必然关系 容器类的工作由think\Container类完成,但大多数情况我们只需要通过app助手函数即可完成大部分操作。 依赖注入其实本质上是指对类的依赖通过构造器完成自动注入,例如在控制器架构方法和操作方法中一旦对参数进行对象类型约束则会自动触发依赖注入,由于

  • 本文向大家介绍.NET IoC模式依赖反转(DIP)、控制反转(Ioc)、依赖注入(DI),包括了.NET IoC模式依赖反转(DIP)、控制反转(Ioc)、依赖注入(DI)的使用技巧和注意事项,需要的朋友参考一下 依赖倒置原则(DIP) 依赖倒置(Dependency Inversion Principle,缩写DIP)是面向对象六大基本原则之一。他是指一种特定的的解耦形式,使得高层次的模块不依

  • 本文向大家介绍Spring的IOC和依赖注入之间的区别。,包括了Spring的IOC和依赖注入之间的区别。的使用技巧和注意事项,需要的朋友参考一下 控制反转是一种设计原则,有助于反转对象创建的控制。 根据马丁·福勒(Martin Fowler)的论文,控制反转是程序控制流反转的原理:外部源(框架,服务,其他组件)代替程序控制程序流,而由程序控制流它。就像我们将某些东西插入其他东西一样。他提到了有关

  • 依赖注入(Dependency Injection,DI)容器就是一个对象,它知道怎样初始化并配置对象及其依赖的所有对象。 Martin 的文章 已经解释了 DI 容器为什么很有用。 这里我们主要讲解 Yii 提供的 DI 容器的使用方法。 依赖注入(Dependency Injection) Yii 通过 yii\di\Container 类提供 DI 容器特性。 它支持如下几种类型的依赖注入:

  • 问题内容: 我想使用适当的依赖项注入来注入对象的字段。我尝试了很多不同的尝试注入等失败的组合。 问题答案: 解析是路由的属性,而不是控制器的属性。控制器将注入在路由级别上定义的依赖项,而无需在控制器上指定解析属性。 以您的一个示例(转换为JavaScript)为例,您将像往常一样定义控制器,即: 然后是路线上的resolve属性: 如果您想使用路由的resolve部分来减少代码,则需要使用数组样式

  • 我正在使用gradle/querydsl和JPA 2.1。 我想使用APT(qenties)生成querydsl元数据。 为此,我使用了gradle apt插件和gradle 4.7 在我的项目中,我使用以下配置了compileJava选项: