Understanding IoC Containers and Dependency Injection
快进:
我写这篇文章的目的是为了更好地理解依赖项注入和IOC容器,同时也为了以后能够纠正其中的错误,并用它来帮助我的一些朋友了解它们。
到目前为止,我已经尝试阅读各种框架的文档(Laravel、Fuel、CodeIgniter、Symfony),我发现框架有太多不同的方面,我需要感到舒适地使用它,所以我决定在尝试在F中使用它们之前,尝试单独学习每个主要部分。拉美工自己。
我花了好几个小时搜索各种含义,查看StackOverflow响应,阅读各种文章,试图了解IOC是什么以及如何使用它来正确管理依赖关系,我相信我理解它的概念,但我仍然对如何正确地实现它感到灰暗。我认为任何人读到这篇文章来帮助我的最好方法就是给出我目前对IOC容器和依赖注入的理解,然后让那些比我有更好理解的人指出我的理解不足之处。
我的理解:
- 依赖性是指类A的实例需要类B的实例来实例化类A的新实例。
- 依赖项注入是当类A通过类A的构造函数中的参数或通过集合~dependencyNamehere~(~dependencyNamehere~$param)函数传递类B的实例时进行的。(这是我不完全确定的领域之一)。
- IOC容器是一个单实例类(在任何给定时间只能有一个实例),在该类中,可以注册为该项目实例化这些类对象的特定方法。下面是一个链接,指向我试图描述的示例,以及我一直使用的IOC容器的类定义。
所以在这一点上,我开始尝试在更复杂的场景中使用IOC容器。到目前为止,似乎为了使用IOC容器,我被限制在HAS关系中,对于我要创建的几乎所有类,它都要在IOC容器中定义依赖关系。如果我想创建一个继承类的类,但是父类是以特定的方式创建的,并且它是在IOC容器中注册的,那该怎么办?
例如:我想创建一个mysqli的子类,但是我想在ioc容器中注册这个类,以便仅用以前在ioc容器中注册的方式构造的父类进行实例化。我想不出一种不复制代码就能做到这一点的方法(因为这是一个学习项目,所以我尽量保持它的"纯粹")。下面是我想描述的更多例子。
下面是我的一些问题:
- 在不违反OOP原则的情况下,我在上面所做的是可行的吗?我知道在C++中,我可以使用动态内存和复制构造函数来完成它,但是在PHP中我还没有找到那种功能。(我承认除了uu construct之外,我几乎没有使用其他任何魔法方法的经验,但是从阅读和u clone开始,如果我理解正确,我就不能在构造函数中使用它来让子类被实例化为父类实例的克隆)。
- 关于国际奥委会,我所有的依赖类定义应该放在哪里?(我的ioc.php应该只在顶部有一堆Require_Once(‘dependencyClassDefinition.php’)吗?我的直觉反应是有更好的方法,但我还没有想出一个)
- 我应该在哪个文件中注册我的对象?目前,在类定义之后调用ioc.php文件中的ioc::register()。
- 在注册需要依赖关系的类之前,我是否需要在IOC中注册依赖关系?因为在实际实例化在IOC中注册的对象之前,我不会调用匿名函数,所以我想不会,但这仍然是一个问题。
- 还有什么我忽视的,我应该做或使用的吗?我试图一步一步地完成它,但我也不想知道我的代码是可重用的,最重要的是,对我的项目一无所知的人可以阅读和理解它。
我知道这是非常漫长的,我只是想提前感谢任何花时间阅读它的人,更重要的是,感谢任何人分享他们的知识。
简单地说(因为这不是一个仅限于OOP世界的问题),依赖性是组件A需要(依赖)组件B来完成它应该做的事情的情况。在这个场景中,这个词还用于描述依赖组件。要用oop/php术语表达这一点,请使用强制汽车类比来考虑以下示例:
1 2 3 4 5 6 7 8 | class Car { public function start() { $engine = new Engine(); $engine->vroom(); } } |
- 这种依赖是隐式的;在检查
Car 的代码之前,您不知道它在那里。 - 这些类是紧密耦合的;不能用
MockEngine 代替Engine 进行测试,也不能用TurboEngine 扩展原始类而不修改Car 。 - 汽车能自己制造发动机,这看起来有点傻,不是吗?
依赖注入是一种解决所有这些问题的方法,它使
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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); |
上面是一个构造函数注入的例子,其中依赖项(依赖对象)通过类构造函数提供给依赖项(使用者)。另一种方法是在
任何一个不平凡的项目都由一系列相互依赖的组件组成,并且很容易对快速注入的内容失去跟踪。依赖注入容器是一个对象,它知道如何实例化和配置其他对象,知道它们与项目中其他对象的关系,并为您进行依赖注入。这使您能够集中管理项目的所有(内部)依赖项,更重要的是,使您可以更改/模拟其中的一个或多个依赖项,而无需编辑代码中的许多位置。
让我们抛开汽车的类比,以OP试图实现的为例。假设我们有一个
1 2 3 4 5 6 7 | $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')); }); |
注意,我们告诉容器从自身抓取EDOCX1的一个实例(15),以组装EDOCX1的一个实例(14)。然后,为了获得一个自动注入依赖项的
1 | $database = $dic->resolve('database'); |
这就是重点。一个稍微复杂一些,但仍然相对简单和容易掌握的php di/ioc容器是疙瘩。查看其文档以获取更多示例。
关于操作规程和问题:
- 不要将静态类或单例用于容器(或其他任何内容);它们都是邪恶的。检查丘疹代替。
- 决定你是想要你的
mysqliWrapper 类扩展mysql 还是依赖它。 - 通过从
mysqliWrapper 内部调用IoC ,您将从一个依赖项交换到另一个依赖项。您的对象不应该知道或使用容器;否则它不再是dic,而是服务定位器(anti)模式。 - 在容器中注册类文件之前,您不需要
require ,因为您根本不知道是否要使用该类的对象。在一个地方设置所有容器。如果不使用自动加载程序,则可以在向容器注册的匿名函数内执行ecx1(26)。
其他资源:
- Martin Fowler的控制容器倒置和依赖注入模式
- 不要找东西--一个关于IOC/DI的干净代码讨论