问题描述
简而言之(因为这不仅仅限于OOP世界), 就是组件A需要(依赖)组件B来完成应做的工作的情况。该词还用于描述这种情况下的依赖组件。为了用OOP / PHP术语来表示,请考虑以下示例,与强制性汽车类比类似:
class Car {
public function start() {
$engine = new Engine();
$engine->vroom();
}
}
Car
依赖 于Engine
。Engine
是Car
的 依赖 。但是这段代码非常糟糕,因为:
- 依赖关系是隐式的;在检查
Car
的代码之前,您不知道它的存在 - 班级紧密相连;你不能代替
Engine
以MockEngine
用于测试目的或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
。这称为“ ,主要用于应该在运行时交换的依赖项。
任何不平凡的项目都由一堆相互依赖的组件组成,并且很容易很快就就无法确定注入了什么。一个 是一个知道如何实例化和配置其他对象的对象,知道他们与项目的其他对象的关系是并执行依赖注入你。这使您可以集中管理所有项目的(相互)依赖关系,更重要的是,可以更改/模拟一个或多个依赖关系,而不必在代码中编辑很多地方。
让我们抛开汽车类比,以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容器。查看其文档以获取更多示例。
- 不要对容器使用静态类或单例(或与此有关的任何其他东西);他们都是邪恶的。请改用Pimple。
- 确定是要 扩展
MysqLiWrapper
类还是要 依赖 它。 - 通过
IoC
从内部调用,MysqLiWrapper
您可以将一个依赖关系交换为另一个依赖关系。您的对象不应该知道或使用容器;否则,它不再是DIC,而是服务定位器(反)模式。 -
require
在将文件注册到容器中之前,不需要类文件,因为您根本不知道是否要使用该类的对象。将所有容器设置放在一个地方。如果您不使用自动加载器,则可以require
在向容器注册的匿名函数内部。
解决方法
我的理解:
- 依赖关系是当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中注册的对象之前,我不会调用匿名函数,所以我猜不是,但这仍然是一个问题。
- 我还有其他需要做的事情吗?我试图一次将其迈出一步,但是我也不想知道我的代码将可重用,最重要的是,一个对我的项目一无所知的人可以阅读并理解它。