依赖注入反转和依赖反转的区别

有两种设计模式,即依赖注入和依赖倒置,网上有文章试图解释这种差异。但是,用更简单的语言解释它的需要仍然存在。外面有人吗?

我需要在 PHP 中理解它。

53255 次浏览

依赖注入是实现控制反转的一种方法(我假设你把这种方法称为依赖性反转) ,所以这两种方法之间的竞争并不激烈,因为依赖注入是 IoC 的一种专门技术。其他常见的实现 IoC 的方法包括使用工厂或服务定位器模式。

请参阅本文 给你

作者用简单的词语将这两者区分开来。依赖注入 = = “ Gim me it”和 Dependency Inversion = = “有人帮我处理这件事,不知怎么的。”.在依赖反转原则中,高级模块是抽象的所有者。因此,细节(抽象的实现)依赖于抽象,因此依赖于高级模块。依赖倒置!..依赖注入不同。抽象可能不由高级模块保存。因此,给予高级对象的抽象可能不限于高级模块的需求。

依赖反转:

您有一个更高层次的模块 X 和一个由 X 定义的抽象 Y。 Z 实现 Y 并给予 X。因此 Z 依赖于 X (通过 X 定义的抽象 Y)。

Dependency injection:

您有一个更高层次的模块 X,它需要功能 A 和 B。 Y 是一个抽象,它包含功能 A,B 和 C。 Z 实现 Y。由于 Z 实现了 Y,因此具有函数 A 和函数 B,所以 Z 给了 X。现在 X 依赖于 Y。

(注意: 这个答案是语言无关的,尽管问题特别提到了 PHP,但由于不熟悉 PHP,我没有提供任何 PHP 示例)

Terminology - Dependencies and Coupling

就面向对象程序设计而言,依赖性是任何其他与类有 直接关系的 对象类型。当一个类 depends直接作用于另一个对象类型时,它可以被描述为该类型的 成双成对

一般来说,类使用的 任何类型在某种程度上是一种依赖。类依赖于另一种类型的方式有很多,包括:

  • 实例变量使用的对象类型
  • 构造函数参数使用的对象类型
  • Object types used by Accessor/Mutator methods
  • 直接创建新对象的构造函数(有时是方法)
  • 遗产

类与其依赖关系越紧密,耦合就越紧密; 因此,当一个类直接依赖于另一个具体类时(比如继承在基类上创建了一个直接依赖关系,或者构造函数为其实例变量创建了新对象) ,这种直接依赖关系的任何未来变化都更有可能以蝴蝶效应的方式“涟漪”传播。


Difference Between Injection vs Inversion

  • 依赖性 注射是一种 返回文章页面控制控制反转译者:技术,通过 依赖注入设计模式向类提供 物品(“依赖性”)。通常通过下列方法之一传递依赖项:

    • A constructor
    • 公共财产公共财产或田地
    • 一只公开的塞特犬
  • 依赖性 Inversion原则(DIP)是一种软件设计 指南,它归结为关于 将类与其具体依赖项解耦的两个建议:

    1. 高级模块不应该依赖于低级模块,两者都应该依赖于抽象
    2. 抽象不应该依赖于细节,细节应该依赖于抽象

或者,更简单地说:

  • 依赖注入是一种实现技术,用于填充类的实例变量。
  • 依赖性反转是一个通用的设计准则,它建议类应该只与高级抽象有直接的关系。

依赖注入及控制反转(IoC)

依赖注入应用 IoC 原则,确保类从不负责创建或提供它们自己的依赖项(因此也不负责这些依赖项的生命周期)。

However, 返回依赖注入页面【强】 IoC 不够强译者: - indeed, IoC as a principle has nothing particularly to do with dependencies or dependency injection per-se; Dependency Injection is a design pattern based around the principle of IoC.

IoC 可以在许多其他上下文中看到,包括那些与对象创建或依赖关系完全无关的上下文,例如通过 Mediator 或消息泵传递消息以触发事件处理程序。IoC 的其他(不相关的)例子包括:

  • 使用事件处理函数/方法处理鼠标/键盘输入事件的窗口应用程序。
  • 使用 Controller Actions 处理 HTTP 请求的 MVC web 应用程序。

(更新自原答案,作为对 IoC 的单独解释)


依赖注入模式

依赖注入是一种应用 IoC 原则的设计模式,以确保类在其构造函数或实例变量所使用的对象的创建或生存期中完全没有参与或意识到——对对象创建和实例变量填充的“常见”关注被推迟到框架中。

也就是说,一个类可以指定它的实例变量,但是不做任何填充这些实例变量的工作(使用构造函数参数作为“传递”除外)

依赖注入为基础设计的类可能是这样的:

// Dependency Injection Example...


class Foo {
// Constructor uses DI to obtain the Meow and Woof dependencies
constructor(fred: Meow, barney: Woof) {
this.fred = fred;
this.barney = barney;
}
}

在本例中,MeowWoof都是通过 Foo构造函数依赖于 注射的。

另一方面,一个设计为 没有依赖注入的 Foo类可能只是简单地创建 MeowWoof实例本身,或者可能使用某种服务定位器/工厂:

// Example without Dependency Injection...


class Foo {
constructor() {
// a 'Meow' instance is created within the Foo constructor
this.fred = new Meow();


// a service locator gets a 'WoofFactory' which in-turn
// is responsible for creating a 'Woof' instance.
// This demonstrates IoC but not Dependency Injection.
var factory = TheServiceLocator.GetWoofFactory();
this.barney = factory.CreateWoof();
}
}

因此,依赖注入只是意味着一个类推迟了 责任获取或提供它自己的依赖项,而这个责任属于任何想要创建一个实例的东西。(通常是 IoC 容器)


依赖反转原则

Dependency Inversion is broadly about de-coupling concrete classes by preventing those classes having any direct reference to each other.

DIP 主要关注的是确保类只依赖于更高级别的抽象。例如,接口存在于比具体类更高的抽象级别。

DIP 并不是注入依赖,尽管依赖注入模式是许多技术之一,可以帮助提供所需的间接级别,以避免依赖于低级别的细节和与其他具体类的耦合。

注意: 依赖性反转在静态类型的编程语言(如 C # 或 Java)中通常更加明确,因为这些语言对变量名强制执行严格的类型检查。另一方面,依赖性反转已经在 Python 或 JavaScript 等动态语言中被动可用,因为这些语言中的变量没有任何特定的类型限制。

考虑静态类型语言中的一个场景,其中一个类需要从应用程序的数据库中读取记录的能力:

// class Foo depends upon a concrete class called SqlRecordReader.


class Foo {
reader: SqlRecordReader;


constructor(sqlReader: SqlRecordReader) {
this.reader = sqlReader;
}


doSomething() {
var records = this.reader.readAll();
// etc.
}
}

在上面的例子中,尽管使用了依赖注入,类 Foo仍然严重依赖于 SqlRecordReader,但它真正关心的是存在一个名为 readAll()的方法,该方法返回一些记录。

考虑这样一种情况: SQL 数据库查询后来被重构为需要更改代码库的单独微服务; Foo类将需要从远程服务读取记录。或者,Foo单元测试需要从内存存储或平面文件中读取数据的情况。

如果像它的名字所暗示的那样,SqlRecordReader包含数据库和 SQL 逻辑,那么任何向微服务的迁移都需要更改 Foo类。

依赖性反转指南建议,SqlRecordReader应该被一个只提供 readAll()方法的更高级别的抽象所替代。即:

interface IRecordReader {
Records[] getAll();
}


class Foo {
reader: IRecordReader;


constructor(reader: IRecordReader) {
this.reader = reader;
}
}

根据 DIP,IRecordReader是一个比 SqlRecordReader, and forcing Footo depend onIRecordReaderinstead ofSqlRecordReader 满足 DIP 指南的 高层抽象。


为什么 DIP 指引有用

关键字是 guideline-依赖性反转为程序的设计增加了间接性。添加任何间接方式的明显缺点是复杂性(即人类理解所发生的事情所需的认知“负荷”)增加。

然而,在许多情况下,间接性可以使代码更容易维护(修复 bug,添加增强) :

In this last example, Foo 也许吧 receive a SqlRecordReader, or maybe a SoapRecordReader, or perhaps a FileRecordReader, or maybe even for unit testing a MockRecordReader - the point is that it doesn't know or care anything about different possible implementations of IRecordReader - provided of course those implementations live up to the Liskov代换原则.

此外,它还避免了这样一种潜在的肮脏场景,即一个急于让某些东西工作的开发人员可能会考虑通过从基类 SqlRecordReader继承 SoapRecordReaderFileRecordReader来“捏造”Liskov 原则。

更糟糕的是,缺乏经验的开发人员甚至可能更改 SqlRecordReader本身,以便类不仅具有用于 SQL 的逻辑,而且具有用于 SOAP 端点、文件系统和可能需要的其他任何东西的逻辑。(这种情况在现实世界中经常发生——特别是在维护不良的代码中,而且几乎总是 代码气味。)

依赖注入是对象提供其他对象的依赖的能力。简单地说,它意味着某些东西依赖于另一些东西。示例类 A 使用了一些类 B 的函数,现在类 A 需要创建类 B 的实例,这里使用 DI。

IOC 是指颠倒不同的职责,例如你需要在家工作,但你需要做饭吃现在列出在家做饭,你可以在网上订购,这意味着你可以专注于你的工作。在这里你把烹饪的责任倒置到网上恢复。

依赖反转原则指出,高级模块不应依赖于低级模块,两者都应依赖于抽象。抽象不应该依赖于细节。细节应该取决于抽象。