一尘不染

了解IoC容器和依赖注入

php

我的理解:

  • 依赖关系是当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中注册的对象之前,我不会调用匿名函数,所以我猜不是,但这仍然是一个问题。
  • 我还有其他需要做的事情吗?我试图一次将其迈出一步,但是我也不想知道我的代码将可重用,最重要的是,一个对我的项目一无所知的人可以阅读并理解它。

阅读 317

收藏
2020-05-29

共1个答案

一尘不染

简而言之(因为这不仅仅限于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在向容器注册的匿名函数内部。
2020-05-29