繁体   English   中英

了解IoC容器和依赖注入

[英]Understanding IoC Containers and Dependency Injection

快进:

我写这篇文章的目的是为了更好地理解依赖注入和IoC容器,以及之后我可以纠正它中的错误并用它来帮助教我的一些朋友关于它们。

截至目前,我已经尝试阅读各种框架(laravel,fuel,codeigniter,symfony)的文档,我发现框架中有太多不同的方面,我需要感觉舒服使用它,我决定尝试在尝试在框架中使用它们之前,先单独学习每个主要部分。

我花了几个小时搜索各种含义,查看stackoverflow响应,阅读各种文章试图了解什么是IoC以及如何使用它来正确管理依赖关系,我相信我理解它在概念中是什么,但我仍然是灰色的关于如何正确实现它。 我认为阅读本文的任何人帮助我的最好方法是给出我目前对IoC容器和依赖注入的理解,然后让那些比我更了解的人指出我的理解不足的地方。

我的理解:

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

所以在这一点上,我开始尝试使用IoC容器来处理更复杂的场景。 到目前为止,为了使用IoC容器,我似乎只限于我想要创建的任何类的has-a关系,它具有它想要在IoC容器中定义的依赖项。 如果我想创建一个继承类的类,但只有在以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 取决于 Engine EngineCar依赖 这段代码非常糟糕,因为:

  • 依赖是隐含的; 在你检查Car的代码之前,你不知道它在那里
  • 班级紧密耦合; 你不能用MockEngine代替Engine用于测试目的,或者TurboEngine可以在不修改Car情况下扩展原始Engine
  • 对于汽车来说,能够为自己制造发动机看起来有点愚蠢,不是吗?

依赖注入是一种解决所有这些问题的方法,它通过使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);

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

任何非平凡的项目都包含一系列相互依赖的组件,很容易在很快就注入的内容上失去跟踪。 依赖注入容器是一个知道如何实例化和配置其他对象的对象,知道它们与项目中其他对象的关系,并为您执行依赖注入。 这使您可以集中管理所有项目(内部)依赖项,更重要的是,可以更改/模拟其中一个或多个,而无需编辑代码中的一堆位置。

让我们放弃汽车比喻,看看OP试图实现的例子。 假设我们有一个依赖于mysqli对象的Database对象。 假设我们想要使用一个非常原始的依赖变量容器类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的代码和问题:

  • 不要为容器使用静态类或单例(或其他任何东西); 他们都是邪恶的 请查看疙瘩。
  • 决定你是否希望你的mysqliWrapper扩展 mysql依赖它。
  • 通过从mysqliWrapper调用IoC ,您可以将一个依赖项交换为另一个。 您的对象不应该知道或使用容器; 否则它不再是DIC它的服务定位器(反)模式。
  • 你并不需要require一个类文件容器中的注册,因为你不知道,如果你打算在所有使用这个类的一个对象之前。 在一个地方完成所有容器设置。 如果你不使用自动加载磁带机,你可以require你与容器注册匿名函数内部。

其他资源:

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM