了解 IoC 容器和依赖注入

2024-01-17

我的理解:

  • 依赖关系是指 Class 的实例需要 Class 的实例来实例化 ClassA 的新实例。
  • 依赖注入是指通过 ClassA 构造函数中的参数或通过 set~DependencyNameHere~(~DependencyNameHere~ $param) 函数向 ClassA 传递 ClassB 的实例。(这是我不完全确定的领域之一).
  • IoC 容器是一个单例类(在任何给定时间只能实例化 1 个实例),可以注册该项目实例化这些类的对象的具体方式。这是我试图描述的示例的链接以及我一直在使用的 IoC 容器的类定义 https://gist.github.com/echochamber/021bb0d59f272ac0f421#file-registering-a-mysqli-object

所以此时我开始尝试使用 IoC 容器来处理更复杂的场景。截至目前,为了使用 IoC 容器,我几乎只能为我想要创建的任何具有想要在 IoC 容器中定义的依赖项的类建立 has-a 关系。如果我想创建一个继承一个类的类,但前提是父类已以特定方式创建并在 IoC 容器中注册,该怎么办?

例如:我想创建 mysqli 的子类,但我想在 IoC 容器中注册此类,以便仅使用我之前在 IoC 容器中注册的方式构造的父类进行实例化。我想不出一种方法可以在不重复代码的情况下做到这一点(并且由于这是一个学习项目,我试图使其尽可能保持“纯粹”)。以下是我试图描述的更多示例。 https://gist.github.com/echochamber/021bb0d59f272ac0f421#file-inheritance-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 depends on Engine. Engine is Car's 依赖性。这段代码非常糟糕,因为:

  • 依赖性是隐式的;你不知道它在那里,直到你检查Car's code
  • 类是紧密耦合的;你不能替代Engine with MockEngine用于测试目的或TurboEngine扩展原来的而不修改Car.
  • 汽车能够自己制造发动机看起来有点傻,不是吗?

依赖注入是解决所有这些问题的一种方法,通过以下事实Car needs 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。这被称为设定注射并且对于应该在运行时交换的依赖关系最有用。

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

让我们放弃汽车类比,以 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');

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


关于OP的代码和问题:

  • 不要为您的容器(或任何其他与此相关的东西)使用静态类或单例;他们都是邪恶的 http://www.youtube.com/watch?v=-FRm3VPhseI。看看粉刺吧。
  • 决定你是否想要你的mysqliWrapper class extend mysql or depend on it.
  • 通过致电IoC从内部mysqliWrapper您正在将一种依赖关系替换为另一种依赖关系。您的对象不应该知道或使用容器;否则它就不再是 DIC,而是服务定位器(反)模式。
  • 你不需要require在将其注册到容器中之前先创建一个类文件,因为您根本不知道是否要使用该类的对象。在一处完成所有容器设置。如果您不使用自动加载器,您可以require在您向容器注册的匿名函数内。

其他资源:

  • 控制容器反转和依赖注入模式 http://www.martinfowler.com/articles/injection.html马丁·福勒
  • 不要寻找东西 http://www.youtube.com/watch?v=RlfLCWKxHJ0-- 关于 IoC/DI 的干净代码讨论
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

了解 IoC 容器和依赖注入 的相关文章

  • Yii 使用 ajax 进行分页

    我需要使用ajax启用分页 我的代码 控制器 更新内容ajax function actionIndex dataProvider new CActiveDataProvider News array pagination gt array
  • 重写某些 .Net Framework 控件的绘制以更改其边框颜色?

    SCENARIO 我正在使用第三方 Windows 视觉主题 当我看到我的应用程序时 它看起来像这样 但是当我使用正常的Aero主题 它看起来到处都有可怕的白色边框 QUESTION 我知道应用程序中使用的配色方案取决于视觉风格 但是 我可
  • Woocommerce 让产品显示在存档页面中

    我正在尝试让所有产品显示在我商店的存档页面中 我想知道他们的id我正在使用我的一个钩子 它在 wp head 上运行并检查 if is product category 我想以某种方式访问 产品的查询并获取它们的 ID if is prod
  • 如何从父类函数访问子类中定义的常量?

    我从 php net 看到这个例子 但 c MY CONST 仅在 5 3
  • Apache 访问 Linux 中的 NTFS 链接文件夹

    在 Debian jessie 中使用 Apache2 PHP 当我想在 Apache 的文档文件夹 var www 中创建一个新的小节时 我只需创建一个指向我的 php 文件所在的外部文件夹的链接 然后只需更改该文件夹的所有者和权限文件夹
  • 扩展蓝图类?

    我想覆盖timestamps 函数中发现Blueprint班级 我怎样才能做到这一点 e g public function up Schema create users function Blueprint table table gt
  • 蛋糕控制台 2.2.1:烘焙错误

    运行 MAMP 的 OSX 机器 CakePHP 2 2 1 已正确安装和配置 这意味着当我浏览到 Index php 文件时 所有绿色条都显示出来 我已经完成了博客教程 并且正在开发我的第二个应用程序 其中脚手架已启动并运行 现在我第一次
  • 是否可以使用 PHP 重定向发送 POST 数据?

    更新 这不是重复的如何使用 PHP 发送 POST 请求 https stackoverflow com questions 5647461 how do i send a post request with php 那里的解决方案对我不起
  • 从 smarty 访问 PHP 文件的变量(本地或全局)

    我有一个 php 文件 其中包含一些本地和全局变量 例如 foo 从此文件中调用 smarty 对象 如何在不更改 PHP 文件的情况下从 smarty 脚本访问 foo Thanks 如果你有一个名为 BASE 的常量变量 并且定义如下
  • 为什么 Delphi 变体不能保存对象?

    为什么 Delphi 变体不能保存对象 更重要的是 这种限制背后的原因是什么 你绝对可以storeVariant 变量中的对象 只需将其转换为 NativeUInt 无论如何 对象只是一个指针 obj TObject Create v Na
  • 学说迁移后备

    我们正在使用原则迁移 当迁移包含多个操作并且其中一个操作失败时 通常会出现问题 例如 如果迁移添加了 5 个外键 其中第 5 个失败 而字段长度不同 则修复字段错误并重新生成迁移不会not修复整个问题 而现在出现一个与 4 个密钥已存在有关
  • 从 .phar 存档中提取文件

    对于 Phar 文件 我完全错过了一些东西 我正在安装一个需要 phpunit pdepend 和其他依赖项的项目 我将它们作为 phar 文件获取 但是 我无法使用命令行工具 php 命令 从中提取文件 我用谷歌搜索了这个问题 但没有发现
  • 在 PHP 中撤销 Google 访问令牌

    正如标题所示 我想以编程方式撤销授予的访问令牌 即在 PHP 中 我发现这个他们的网站 https developers google com identity protocols OAuth2WebServer tokenrevoke 但
  • 在 apache docker 容器中运行虚拟主机

    我在同一个 apache 容器中有两个 php 应用程序 我试图在端口上运行其中一个应用程序 因为它需要通过根域而不是子文件夹进行访问 我想在端口 8060 上运行应用程序 我尝试使用 apache 虚拟主机执行此操作 但它不会加载页面 h
  • 跟踪用户何时点击浏览器上的后退按钮

    是否可以检测用户何时单击浏览器的后退按钮 我有一个 Ajax 应用程序 如果我可以检测到用户何时单击后退按钮 我可以显示适当的数据 任何使用 PHP JavaScript 的解决方案都是优选的 任何语言的解决方案都可以 只需要我可以翻译成
  • 使用接口有什么好处?

    使用接口有什么用 我听说它用来代替多重继承 并且还可以用它来完成数据隐藏 还有其他优点吗 哪些地方使用了接口 程序员如何识别需要该接口 有什么区别explicit interface implementation and implicit
  • Laravel 中只向登录用户显示按钮

    如果我以 John 身份登录 如何才能只显示 John 的红色按钮而不显示 Susan 的红色按钮 测试系统环境 Win10 Laravel5 4 Mysql5 7 19 table class table table responsive
  • 使用正则表达式提取两个短语之间的所有单词[重复]

    这个问题在这里已经有答案了 我正在尝试使用以下正则表达式提取两个短语之间的所有单词 b item W w W 0 2 1 one W w W 0 3 business b b item W w W 0 2 3 three W w W 0 3
  • 简单的 PHP 表单:电子邮件附件(代码 Golf)

    想象一下 一个用户想要在其网站上放置一个表单 该表单将允许网站访问者上传一个文件和一条简单的消息 该消息将立即通过电子邮件发送 即 该文件未存储在服务器上 或者如果该文件存储在服务器上 仅暂时 作为文件附件 并在邮件正文中添加注释 查看更多
  • 如何使用 PHPExcel 库从 Excel 获取日期

    我正在尝试使用 PHPExcel 从 Excel 获取日期 但我没有得到日期 我得到的字符串值不是 1970 以来的秒数 我尝试过的代码是 InvDate trim excel gt getActiveSheet gt getCell B

随机推荐

  • python中的逐元素张量乘法

    我正在尝试使用 python 解决计算代数中的问题 基本上给出了两组 比如说A a b and B e 我需要逐个元素地计算张量积并得到最终的集合C a tensor e b tensor e 含有这些元素的产物 我可以使用带有数字的数组进
  • 如何在gitlab ci中引用作业规则中的变量?

    我需要在 gitlab ci 作业规则中重用变量 include template Workflows Branch Pipelines gitlab ci yml staging variables variables CONFIG NA
  • 为什么 Vite 会创建两个 TypeScript 配置文件:tsconfig.json 和 tsconfig.node.json?

    我正在使用Vite来创建一个新的 React TypeScript 项目 https vitejs dev guide scaffolding your first vite project 创建项目后 根文件夹中有两个 TypeScrip
  • 将字典转换为 url 参数字符串?

    有没有办法将代码中的字典转换为url参数字符串 e g An example list of parameters Dictionary
  • Xcode 8 要求注册设备以创建配置文件

    我正在尝试发布一个应用程序进行测试 而无需在我的 Mac 中连接设备 我创建了一个开发者帐户 在 itunes 中创建了一个应用程序 生成了捆绑包 ID 使用开发者帐户登录到 xcode 生成了经销商证书 当我尝试存档应用程序时 出现错误
  • @ViewBag 在当前上下文中不存在

    我知道网上已经有一些解决方案 但在尝试了所有解决方案后 我对自己感到困惑 我只是想澄清我尝试过的解决方案 确保Microsoft AspNet Web Optimization or System Web Optimization有没有 我
  • 将焦点交给浏览器

    我正在研究键盘辅助功能 我有一个位于页面内的 Flash 对象 为了防止焦点被困在其中 我添加了可以进行对话的选项卡侦听器ExternalInterface一些 JavaScript 函数 JavaScript 会查找下一个可用元素tabI
  • PostgreSQL 9.3:如何将大写 UUID 插入表中

    我有下表 其中只有 1 列id这是类型UUID Table uuidtest create table uuidtest id uuid 插入 我已经使用生成了uuiduuid generate v4 并且也做了大写 并将其插入表中 Try
  • CascadeType.ALL、cascade = CascadeType.REMOVE 和 orphanRemoval 之间有什么区别

    我搜索了答案 但无法正确找到答案 有什么区别CascadeType ALL 级联 CascadeType REMOVE orphanRemoval当我们设置FetchType EAGER on OneToMany关系 有一次我在删除记录时遇
  • 自动完成未触发

    下面看起来一切都很好 我已经提供了 3 个部分 如果查询的话 服务工作正常
  • 在 Lua 中弹出数组的第一个元素

    我有一个数组x在卢阿 我想设置head x 1 and rest 数组的其余部分 以便rest 1 x 2 rest 2 x 3 etc 我怎样才能做到这一点 注意 我不在乎原始数组是否发生变异 在 Javascript 中我会这样做hea
  • jQuery 选择插件:自定义/格式化选择中选项的文本

    我在 jquery 中使用一个名为 selected 的插件 用于具有自动完成功能的选择框 http harvesthq github com chosen http harvesthq github com chosen 在哪里可以设置选
  • 抑制可忽略不计的复杂 numpy 特征值?

    我正在计算协方差矩阵的特征值 该矩阵是实数且对称正半定的 因此 特征值和特征向量应该都是实数 但是numpy linalg eig https docs scipy org doc numpy reference generated num
  • 将 List 转换为 List

    理解了这个概念翻译 https groups google com forum msg servicestack BF egdVm3M8 0DXLIeDoVJEJ 使用它来将 DataModel 类型转换为表示层的 DTO 类型 这样效果很
  • 扩展 CLPlacemark 会导致 EXC BAD ACCESS

    虽然发现了类似的问题here https stackoverflow com questions 20204417 exc bad access after populating nsmutablearray with custom cla
  • 如何使用 SwiftUI 在 NavigationView 中正确包含“添加项目”按钮?

    我需要一个 加号 按钮NavigationView s List navigationBarItems 在导航栏右侧 我想在列表中添加一行 使用导航层次结构中的后续视图输入其名称等 但首先 我什至无法让按钮正确导航 当我在预览画布中点击它时
  • 我们应该如何管理jdk8流的空值

    我知道这个话题可能有点in advance因为 JDK8 尚未发布 至少现在还没有发布 但我正在阅读一些有关 Lambda 表达式的文章 特别是与称为 Stream 的新集合 API 相关的部分 这是中给出的示例Java 杂志文章 http
  • 如何在gradle中下载依赖项

    我有一个自定义编译任务 task compileSpeedTest type JavaCompile classpath files build source fileTree src test java speed destination
  • 安卓相机预览

    我有个问题 我正在尝试开发一个使用相机的程序 一切都在我的设备中运行 但就像你们很多人都知道的那样CameraPreview并非在所有设备上都以相同的方式工作 因此我尝试集成谷歌在此地址中提供的代码 http developer andro
  • 了解 IoC 容器和依赖注入

    我的理解 依赖关系是指 Class 的实例需要 Class 的实例来实例化 ClassA 的新实例 依赖注入是指通过 ClassA 构造函数中的参数或通过 set DependencyNameHere DependencyNameHere