了解IoC容器和依赖注入

快速前进:

我写这个的目的是为了更好地理解依赖注入和IoC容器,但是之后我可以纠正它中的错误,并用它来帮助我的几个朋友教导它们。

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

我花了数小时搜索各种各样的含义,查看了stackoverflow响应,并阅读了各种文章试图了解什么是IoC以及如何使用它来正确管理依赖关系,并且我相信我理解它在概念上是什么,但我仍然是灰色的关于如何正确实施它。 我认为读这本书帮助我的最佳方式是给出我目前对IoC容器和依赖注入的理解,然后让比我更好理解的人指出我的理解缺失的地方。

我的理解:

  • 依赖关系是当ClassA的实例需要ClassB的实例来实例化ClassA的新实例时。
  • 依赖注入是当ClassA通过ClassA的构造函数中的参数或通过set〜DependencyNameHere〜(〜DependencyNameHere〜$ param)函数传递ClassB的实例时。 (这是我不完全确定的领域之一)。
  • 一个IoC容器是一个单例类(在任何给定时间只能有一个实例实例化),其中实例化这个类的对象的具体方式可以注册。 下面是我想要描述的一个例子的链接,以及我一直使用的IoC容器的类定义
  • 所以在这里我开始尝试使用IoC容器来处理更复杂的场景。 到目前为止,为了使用IoC容器,我仅限于几乎任何我想创建的具有依赖关系的类的关系,这些依赖关系是要在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来执行它应该做的事情的情况。 这个词也被用来描述这个场景中依赖的组件。 要把它放在面向对象/ 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);
    

    以上是构造函数注入的一个例子,其中通过类构造函数将依赖项(依赖对象)提供给依赖项(消费者)。 另一种方法是暴露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');
    

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


    关于OP的代码和问题:

  • 不要为你的容器使用静态类或单例(或者对于其他任何事情); 他们都是邪恶的。 取而代之的是Pimple。
  • 决定你是否希望你的mysqliWrapper类扩展mysql或依赖它。
  • 通过在mysqliWrapper调用IoC ,可以交换一个依赖关系。 你的物体不应该知道或使用容器; 否则它不再是DIC,而是服务定位器(反)模式。
  • 在将容器注册到容器中之前, require使用类文件,因为您不知道是否要使用该类的对象。 在一个地方做所有的容器设置。 如果您不使用自动加载器,则可以在注册到容器的匿名函数内部进行require

  • 其他资源:

  • 控制容器的反转和Martin Fowler的依赖注入模式
  • 不要找东西 - 关于IoC / DI的Clean Code Talk
  • 链接地址: http://www.djcxy.com/p/82145.html

    上一篇: Understanding IoC Containers and Dependency Injection

    下一篇: Are primitive constructor parameters a bad idea when using an IoC Container?