什么是依赖注入?
已经发布了几个关于依赖注入的具体问题,例如何时使用它以及有哪些框架。 但是,
什么是依赖注入以及何时/为什么应该或不应该使用它?
There have been several questions already posted with specific questions about dependency injection, such as when to use it and what frameworks are there for it. However,
What is dependency injection and when/why should or shouldn't it be used?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(30)
我知道已经有很多答案,但我发现这非常有帮助: http://tutorials .jenkov.com/dependency-injection/index.html
无依赖关系:
依赖关系:
注意如何将
DataSourceImpl
实例化移动到构造函数中。 构造函数采用四个参数,即 DataSourceImpl 所需的四个值。 尽管MyDao
类仍然依赖于这四个值,但它本身不再满足这些依赖关系。 它们由创建 MyDao 实例的任何类提供。I know there are already many answers, but I found this very helpful: http://tutorials.jenkov.com/dependency-injection/index.html
No Dependency:
Dependency:
Notice how the
DataSourceImpl
instantiation is moved into a constructor. The constructor takes four parameters which are the four values needed by theDataSourceImpl
. Though theMyDao
class still depends on these four values, it no longer satisfies these dependencies itself. They are provided by whatever class creating aMyDao
instance.摘自Apress.Spring.Persistence.with.Hibernate.Oct.2010
一书
from Book Apress.Spring.Persistence.with.Hibernate.Oct.2010
到目前为止,我找到的最好的定义是 James Shore 的定义:
Martin Fowler 的一篇文章也可能有用。
依赖注入基本上是提供对象所需的对象(其依赖项),而不是让它自己构造它们。 这是一种非常有用的测试技术,因为它允许模拟或删除依赖项。
可以通过多种方式将依赖项注入到对象中(例如构造函数注入或 setter 注入)。 人们甚至可以使用专门的依赖注入框架(例如Spring)来做到这一点,但它们当然不是必需的。 您不需要这些框架来进行依赖项注入。 显式实例化和传递对象(依赖项)与框架注入一样好。
The best definition I've found so far is one by James Shore:
There is an article by Martin Fowler that may prove useful, too.
Dependency injection is basically providing the objects that an object needs (its dependencies) instead of having it construct them itself. It's a very useful technique for testing, since it allows dependencies to be mocked or stubbed out.
Dependencies can be injected into objects by many means (such as constructor injection or setter injection). One can even use specialized dependency injection frameworks (e.g. Spring) to do that, but they certainly aren't required. You don't need those frameworks to have dependency injection. Instantiating and passing objects (dependencies) explicitly is just as good an injection as injection by framework.
依赖注入是将依赖传递给其他对象或框架(依赖注入器)。
依赖注入使测试变得更加容易。 注入可以通过构造函数完成。
SomeClass()
的构造函数如下:问题:
如果
myObject
涉及磁盘访问或网络访问等复杂任务,则对SomeClass()
进行单元测试是困难的。 程序员必须模拟myObject
并且可能拦截工厂调用。替代解决方案:
myObject
作为参数传递给构造函数myObject
可以直接传递,这使得测试更容易。如果没有依赖注入,在单元测试中隔离组件会更加困难。
2013 年,当我写下这个答案时,这是 Google 测试博客 上的一个主要主题。 这对我来说仍然是最大的优势,因为程序员并不总是需要在运行时设计中具有额外的灵活性(例如,服务定位器或类似的模式)。 程序员经常需要在测试期间隔离类。
Dependency Injection is passing dependency to other objects or framework( dependency injector).
Dependency injection makes testing easier. The injection can be done through constructor.
SomeClass()
has its constructor as following:Problem:
If
myObject
involves complex tasks such as disk access or network access, it is hard to do unit test onSomeClass()
. Programmers have to mockmyObject
and might intercept the factory call.Alternative solution:
myObject
in as an argument to the constructormyObject
can be passed directly which makes testing easier.It is harder to isolate components in unit testing without dependency injection.
In 2013, when I wrote this answer, this was a major theme on the Google Testing Blog. It remains the biggest advantage to me, as programmers not always need the extra flexibility in their run-time design (for instance, for service locator or similar patterns). Programmers often need to isolate the classes during testing.
我在松散耦合方面发现了这个有趣的例子:
来源:< em>了解依赖注入
任何应用程序由许多对象组成,这些对象相互协作以执行一些有用的操作。 传统上,每个对象负责获取对其协作的依赖对象(依赖项)的自己的引用。 这导致了高度耦合的类和难以测试的代码。
例如,考虑一个
Car
对象。汽车
依靠车轮、发动机、燃料、电池等来运行。 传统上,我们定义此类依赖对象的品牌以及Car
对象的定义。没有依赖注入 (DI):
这里,
Car
对象负责创建依赖对象。如果我们想要更改对象的类型该怎么办?它的依赖对象 - 比如说
Wheel
- 在最初的NepaliRubberWheel()
刺穿之后?我们需要使用新的依赖项(如
ChineseRubberWheel()
)重新创建 Car 对象,但只有Car
制造商可以做到这一点。那么
依赖注入
为我们做了什么......?当使用依赖注入时,对象在运行时被赋予它们的依赖关系而不是编译时间(汽车制造时间)。
这样我们现在就可以随时更改
Wheel
。 在这里,可以在运行时将依赖项
(wheel
)注入到Car
中。使用依赖项注入后:
在这里,我们在运行时注入依赖项(车轮和电池)。 因此出现了术语:依赖注入。我们通常依赖 Spring、Guice、Weld 等 DI 框架来创建依赖项并在需要时进行注入。
优点是:
I found this funny example in terms of loose coupling:
Source: Understanding dependency injection
Any application is composed of many objects that collaborate with each other to perform some useful stuff. Traditionally each object is responsible for obtaining its own references to the dependent objects (dependencies) it collaborate with. This leads to highly coupled classes and hard-to-test code.
For example, consider a
Car
object.A
Car
depends on wheels, engine, fuel, battery, etc. to run. Traditionally we define the brand of such dependent objects along with the definition of theCar
object.Without Dependency Injection (DI):
Here, the
Car
object is responsible for creating the dependent objects.What if we want to change the type of its dependent object - say
Wheel
- after the initialNepaliRubberWheel()
punctures?We need to recreate the Car object with its new dependency say
ChineseRubberWheel()
, but only theCar
manufacturer can do that.Then what does the
Dependency Injection
do for us...?When using dependency injection, objects are given their dependencies at run time rather than compile time (car manufacturing time).
So that we can now change the
Wheel
whenever we want. Here, thedependency
(wheel
) can be injected intoCar
at run time.After using dependency injection:
Here, we are injecting the dependencies (Wheel and Battery) at runtime. Hence the term : Dependency Injection. We normally rely on DI frameworks such as Spring, Guice, Weld to create the dependencies and inject where needed.
The advantages are:
依赖注入是一种实践,其中对象的设计方式是从其他代码段接收对象的实例,而不是在内部构造它们。 这意味着可以在不更改代码的情况下替换实现该对象所需接口的任何对象,从而简化了测试并提高了解耦性。
例如,考虑以下类:
在此示例中,
PersonService::addManager
和PersonService::removeManager
的实现需要GroupMembershipService
的实例> 以便完成其工作。 如果没有依赖注入,传统的方法是在 PersonService 的构造函数中实例化一个新的 GroupMembershipService ,并在两个函数中使用该实例属性。 但是,如果GroupMembershipService
的构造函数需要多个东西,或者更糟糕的是,需要在GroupMembershipService
上调用一些初始化“setter”,那么代码就会增长速度相当快,PersonService
现在不仅依赖于GroupMembershipService
,还依赖于GroupMembershipService
所依赖的其他所有内容。 此外,与GroupMembershipService
的链接被硬编码到PersonService
中,这意味着您无法出于测试目的“虚拟”GroupMembershipService
,或者在应用程序的不同部分使用策略模式。通过依赖注入,您可以将其传递给
PersonService
构造函数,或者添加一个属性(getter 和 setter)来设置它的本地实例。 这意味着您的PersonService
不再需要担心如何创建GroupMembershipService
,它只接受给定的那些,并与它们一起工作。 这也意味着GroupMembershipService
的子类或实现GroupMembershipService
接口的任何内容都可以“注入”到PersonService
中,并且 < code>PersonService 不需要知道更改。Dependency Injection is a practice where objects are designed in a manner where they receive instances of the objects from other pieces of code, instead of constructing them internally. This means that any object implementing the interface which is required by the object can be substituted in without changing the code, which simplifies testing, and improves decoupling.
For example, consider these clases:
In this example, the implementation of
PersonService::addManager
andPersonService::removeManager
would need an instance of theGroupMembershipService
in order to do its work. Without Dependency Injection, the traditional way of doing this would be to instantiate a newGroupMembershipService
in the constructor ofPersonService
and use that instance attribute in both functions. However, if the constructor ofGroupMembershipService
has multiple things it requires, or worse yet, there are some initialization "setters" that need to be called on theGroupMembershipService
, the code grows rather quickly, and thePersonService
now depends not only on theGroupMembershipService
but also everything else thatGroupMembershipService
depends on. Furthermore, the linkage toGroupMembershipService
is hardcoded into thePersonService
which means that you can't "dummy up" aGroupMembershipService
for testing purposes, or to use a strategy pattern in different parts of your application.With Dependency Injection, instead of instantiating the
GroupMembershipService
within yourPersonService
, you'd either pass it in to thePersonService
constructor, or else add a Property (getter and setter) to set a local instance of it. This means that yourPersonService
no longer has to worry about how to create aGroupMembershipService
, it just accepts the ones it's given, and works with them. This also means that anything which is a subclass ofGroupMembershipService
, or implements theGroupMembershipService
interface can be "injected" into thePersonService
, and thePersonService
doesn't need to know about the change.让我们尝试一下 Car 和 Engine 类的简单示例,任何汽车都需要引擎才能到达任何地方,至少目前是这样。 下面是在没有依赖注入的情况下代码的外观。
为了实例化 Car 类,我们将使用下一个代码:
此代码的问题是我们与 GasEngine 紧密耦合,如果我们决定将其更改为 ElectricityEngine,那么我们将需要重写 Car 类。 应用程序越大,我们将不得不添加和使用新型引擎的问题和头痛就越多。
换句话说,通过这种方法,我们的高级 Car 类依赖于较低级别的 GasEngine 类,这违反了 SOLID 的依赖倒置原则 (DIP)。 DIP 建议我们应该依赖抽象,而不是具体的类。 因此,为了满足这一点,我们引入了 IEngine 接口并重写了如下代码:
现在我们的 Car 类仅依赖于 IEngine 接口,而不依赖于引擎的具体实现。
现在,唯一的技巧是我们如何创建 Car 的实例并为其提供一个实际的具体 Engine 类,如 GasEngine 或 ElectricityEngine。 这就是依赖注入的用武之地。
这里我们基本上将我们的依赖项(Engine 实例)注入(传递)到 Car 构造函数。 因此,现在我们的类在对象及其依赖项之间具有松散耦合,我们可以轻松添加新类型的引擎,而无需更改 Car 类。
依赖注入的主要好处是类的耦合更加松散,因为它们没有硬编码的依赖关系。 这遵循上面提到的依赖倒置原则。 类不是引用特定的实现,而是请求抽象(通常是接口),这些抽象在构造类时提供给它们。
此外,当我们有很多依赖项时,使用控制反转(IoC)容器是非常好的做法,我们可以知道哪些接口应该映射到所有依赖项的哪些具体实现,并且我们可以让它在构造时为我们解析这些依赖项我们的对象。 例如,我们可以在 IoC 容器的映射中指定 IEngine 依赖项应映射到 GasEngine 类,并且当我们向 IoC 容器请求我们的实例时Car 类,它将自动构造我们的 Car 类,并传入 GasEngine 依赖项。
更新: 观看了有关的课程Julie Lerman 最近提供的 EF Core,也喜欢她关于 DI 的简短定义。
Let's try simple example with Car and Engine classes, any car need an engine to go anywhere, at least for now. So below how code will look without dependency injection.
And to instantiate the Car class we will use next code:
The issue with this code that we tightly coupled to GasEngine and if we decide to change it to ElectricityEngine then we will need to rewrite Car class. And the bigger the application the more issues and headache we will have to add and use new type of engine.
In other words with this approach is that our high level Car class is dependent on the lower level GasEngine class which violate Dependency Inversion Principle(DIP) from SOLID. DIP suggests that we should depend on abstractions, not concrete classes. So to satisfy this we introduce IEngine interface and rewrite code like below:
Now our Car class is dependent on only the IEngine interface, not a specific implementation of engine.
Now, the only trick is how do we create an instance of the Car and give it an actual concrete Engine class like GasEngine or ElectricityEngine. That's where Dependency Injection comes in.
Here we basically inject(pass) our dependency(Engine instance) to Car constructor. So now our classes have loose coupling between objects and their dependencies, and we can easily add new types of engines without changing the Car class.
The main benefit of the Dependency Injection that classes are more loosely coupled, because they do not have hard-coded dependencies. This follows the Dependency Inversion Principle, which was mentioned above. Instead of referencing specific implementations, classes request abstractions (usually interfaces) which are provided to them when the class is constructed.
Also when we have many dependencies it is very good practice to use Inversion of Control(IoC) containers which we can tell which interfaces should be mapped to which concrete implementations for all our dependencies and we can have it resolve those dependencies for us when it constructs our object. For example, we could specify in the mapping for the IoC container that the IEngine dependency should be mapped to the GasEngine class and when we ask the IoC container for an instance of our Car class, it will automatically construct our Car class with a GasEngine dependency passed in.
UPDATE: Watched course about EF Core from Julie Lerman recently and also liked her short definition about DI.
接受的答案是一个很好的答案 - 但我想补充一点,DI 非常类似于在代码中避免硬编码常量的经典做法。
当您使用一些常量(例如数据库名称)时,您可以快速将其从代码内部移动到某个配置文件,并将包含该值的变量传递到需要的地方。 这样做的原因是这些常量通常比代码的其余部分更频繁地更改。 例如,如果您想在测试数据库中测试代码。
DI 与面向对象编程领域中的情况类似。 那里的值而不是常量文字是整个对象 - 但将创建它们的代码从类代码中移出的原因是相似的 - 对象的更改比使用它们的代码更频繁。 需要进行此类更改的一个重要情况是测试。
The accepted answer is a good one - but I would like to add to this that DI is very much like the classic avoiding of hardcoded constants in the code.
When you use some constant like a database name you'd quickly move it from the inside of the code to some config file and pass a variable containing that value to the place where it is needed. The reason to do that is that these constants usually change more frequently than the rest of the code. For example if you'd like to test the code in a test database.
DI is analogous to this in the world of Object Oriented programming. The values there instead of constant literals are whole objects - but the reason to move the code creating them out from the class code is similar - the objects change more frequently then the code that uses them. One important case where such a change is needed is tests.
在进行技术描述之前,首先用一个现实生活中的例子来形象化它,因为你会发现很多技术东西来学习依赖注入,但大多数人无法理解它的核心概念。
在第一张图片中,假设您有一个拥有大量车辆的汽车工厂。 汽车实际上是在装配单元中制造出来的,但它需要发动机、座椅以及车轮。 因此,装配单元依赖于所有这些单元,并且它们是工厂的依赖性。
你会觉得现在维护这个工厂的所有任务太复杂了,因为除了主要任务(在装配单元组装汽车)之外,你还必须关注其他单元。 现在维护成本非常高,而且厂房很大,所以需要额外的租金。
现在,看第二张图。 如果您发现一些供应商公司可以为您提供车轮、座椅和发动机,并且价格比您自己生产的成本更便宜,那么现在您不需要在你的工厂制造它们。 您现在可以为您的装配单元租用较小的建筑物,这将减少您的维护任务并减少额外的租赁成本。 现在您也可以只专注于您的主要任务(汽车组装)。
现在我们可以说组装汽车的所有依赖项都是从提供商注入到工厂的。 这是现实生活中依赖注入 (DI) 的示例。
现在用技术术语来说,依赖注入是一种技术,一个对象(或静态方法)通过该技术提供另一个对象的依赖关系。 所以,把创建对象的任务交给别人,直接使用依赖就叫做依赖注入。
这现在将通过技术解释帮助您学习 DI。 这将显示何时使用 DI 以及何时使用你不应该不。
。
Before going to the technical description first visualize it with a real-life example because you will find a lot of technical stuff to learn dependency injection but the majority of the people can't get the core concept of it.
In the first picture, assume that you have a car factory with a lot of units. A car is actually built in the assembly unit but it needs engine, seats as well as wheels. So an assembly unit is dependent on these all units and they are the dependencies of the factory.
You can feel that now it is too complicated to maintain all of the tasks in this factory because along with the main task (assembling a car in the Assembly unit) you have to also focus on other units. It is now very costly to maintain and the factory building is huge so it takes your extra bucks for rent.
Now, look at the second picture. If you find some provider companies that will provide you with the wheel, seat, and engine for cheaper than your self-production cost then now you don't need to make them in your factory. You can rent a smaller building now just for your assembly unit which will lessen your maintenance task and reduce your extra rental cost. Now you can also focus only on your main task (Car assembly).
Now we can say that all the dependencies for assembling a car are injected on the factory from the providers. It is an example of a real-life Dependency Injection (DI).
Now in the technical word, dependency injection is a technique whereby one object (or static method) supplies the dependencies of another object. So, transferring the task of creating the object to someone else and directly using the dependency is called dependency injection.
This will help you now to learn DI with a technical explanation. This will show when to use DI and when you should not.
.
这是关于依赖注入和依赖注入最简单的解释容器我见过:
没有依赖注入
...),所以:
使用依赖注入执行
使用依赖注入容器执行
依赖注入和依赖注入容器是不同的事情:
您不需要容器来进行依赖项注入。 然而,容器可以帮助你。
This is the most simple explanation about Dependency Injection and Dependency Injection Container I have ever seen:
Without Dependency Injection
…), so:
With Dependency Injection
Using a Dependency Injection Container
Dependency Injection and dependency Injection Containers are different things:
You don't need a container to do dependency injection. However a container can help you.
让我们想象一下,您想去钓鱼:
如果没有依赖注入,您需要自己处理所有事情。 你需要找一条船,买一根鱼竿,寻找鱼饵等等。当然这是可能的,但这会让你承担很多责任。 用软件术语来说,这意味着您必须对所有这些事情执行查找。
通过依赖注入,其他人会负责所有准备工作并为您提供所需的设备。 您将收到(“被注入”)船、钓鱼竿和鱼饵 - 一切都可以使用。
Let's imagine that you want to go fishing:
Without dependency injection, you need to take care of everything yourself. You need to find a boat, to buy a fishing rod, to look for bait, etc. It's possible, of course, but it puts a lot of responsibility on you. In software terms, it means that you have to perform a lookup for all these things.
With dependency injection, someone else takes care of all the preparation and makes the required equipment available to you. You will receive ("be injected") the boat, the fishing rod and the bait - all ready to use.
“依赖注入”不就是意味着使用参数化构造函数和公共设置器吗?
James Shore 的文章展示了以下示例进行比较。
Doesn't "dependency injection" just mean using parameterized constructors and public setters?
James Shore's article shows the following examples for comparison.
使依赖注入概念变得简单易懂。 让我们以开关按钮来切换(开/关)灯泡为例。
如果没有依赖注入,
开关需要事先知道我连接到哪个灯泡(硬编码依赖)。 所以,
切换-> PermanentBulb //开关直接连接到永久灯泡,无法轻松测试
使用依赖注入
开关只知道我需要打开/关闭传递给我的灯泡。 所以,
切换-> Bulb1 OR Bulb2 OR NightBulb(注入依赖项)
修改开关和灯泡的 James 示例:
To make Dependency Injection concept simple to understand. Let's take an example of switch button to toggle(on/off) a bulb.
Without Dependency Injection
Switch needs to know beforehand which bulb I am connected to (hard-coded dependency). So,
Switch -> PermanentBulb //switch is directly connected to permanent bulb, testing not possible easily
With Dependency Injection
Switch only knows I need to turn on/off whichever Bulb is passed to me. So,
Switch -> Bulb1 OR Bulb2 OR NightBulb (injected dependency)
Modifying James Example for Switch and Bulb:
什么是依赖注入 (DI)?
正如其他人所说,依赖注入 (DI) 消除了直接创建其他对象实例以及管理其生命周期的责任我们感兴趣的类(消费者类)是依赖的(在 UML 意义上)。 这些实例通常作为构造函数参数或通过属性设置器传递给我们的消费者类(依赖对象实例化和传递给消费者类的管理通常由控制反转 (IoC) 执行)容器,但那是另一个话题)。
DI、DIP 和 SOLID
具体来说,在 Robert C Martin 的 面向对象设计的SOLID原则,
DI
是依赖倒置原则(DIP)。 DIP 是SOLID
口头禅的D
- 其他 DIP实现包括服务定位器和插件模式。DIP 的目标是解耦类之间紧密、具体的依赖关系,而是通过抽象来放松耦合,这可以通过
接口
、抽象类
来实现code> 或纯虚拟类
,具体取决于所使用的语言和方法。如果没有 DIP,我们的代码(我称之为“消费类”)将直接耦合到具体的依赖项,并且通常还承担着了解如何获取和管理此依赖项的实例的责任,即概念上:
而应用 DIP 后,要求放松了,并且消除了获取和管理 Foo 依赖项的生命周期的问题:
为什么使用 DIP(和 DI)?
以这种方式解耦类之间的依赖关系允许用其他也满足抽象先决条件的实现轻松替换这些依赖类(例如,可以使用同一接口的另一个实现来切换依赖关系)。 此外,正如其他人所提到的,通过 DIP 解耦类的最常见原因可能是允许单独测试消费类,因为现在可以对这些相同的依赖项进行存根和/或模拟。
DI 的后果之一是依赖对象实例的生命周期管理不再由消费类控制,因为依赖对象现在被传递到消费类(通过构造函数或 setter 注入)。
这可以用不同的方式来看待:
Create
获取实例,并在完成后处理这些实例。何时使用 DI?
MyDepClass
是线程安全的 - 如果我们将其设为单例并向所有使用者注入相同的实例会怎样?)示例
这是一个简单的 C# 实现。 给出下面的 Consuming 类:
虽然看似无害,但它对另外两个类(
System.DateTime
和System.Console
)有两个static
依赖项,这两个类不仅限制了日志输出选项(如果没有人在看,日志记录到控制台将毫无价值),而且更糟糕的是,由于对非确定性系统时钟的依赖,很难自动测试。然而,我们可以将
DIP
应用于此类,通过将时间戳问题抽象为依赖关系,并将MyLogger
仅耦合到一个简单的接口:我们还可以放松依赖关系在
Console
上到抽象,例如TextWriter
。 依赖注入通常实现为构造函数注入(将抽象传递给依赖项作为参数传递给消费类的构造函数)或 Setter 注入(通过setXyz()
setter 或定义了{set;}
的 .Net 属性)。 构造函数注入是首选,因为这可以保证类在构造后处于正确的状态,并允许将内部依赖字段标记为readonly
(C#) 或final
(爪哇)。 因此,在上面的示例中使用构造函数注入,这给我们留下了:(需要提供具体的
Clock
,它当然可以恢复为DateTime.Now
,并且两个依赖项需要由 IoC 容器通过构造函数注入提供)可以构建自动单元测试,这明确地证明我们的记录器工作正常,因为我们现在可以控制依赖项 - 时间,并且我们可以监视编写的内容输出:
后续步骤
依赖注入总是与控制反转容器 (IoC) 相关联,注入(提供)具体的依赖实例,并管理实例的生命周期。 在配置/引导过程中,IoC 容器允许定义以下内容:
ConcreteBar
实例”)通常,一旦配置/引导 IoC 容器,它们就会在后台无缝运行,从而使编码人员能够专注于手头的代码,而不必担心依赖关系。
如上面的示例,依赖项的解耦确实需要一些设计工作,对于开发人员来说,有一个范例这种转变需要打破直接新建依赖项的习惯,而是信任容器来管理依赖项。
但好处有很多,特别是能够彻底测试您感兴趣的类别。
注意:POCO / POJO /序列化DTO /实体图/匿名JSON投影等的创建/映射/投影(通过
new ..()
) - 即“数据”仅“类或记录 - 从方法中使用或返回不被视为依赖项(在 UML 意义上)并且不受 DI 约束。 使用 new 来投影这些就可以了。What is Dependency Injection (DI)?
As others have said, Dependency Injection(DI) removes the responsibility of direct creation, and management of the lifespan, of other object instances upon which our class of interest (consumer class) is dependent (in the UML sense). These instances are instead passed to our consumer class, typically as constructor parameters or via property setters (the management of the dependency object instancing and passing to the consumer class is usually performed by an Inversion of Control (IoC) container, but that's another topic).
DI, DIP and SOLID
Specifically, in the paradigm of Robert C Martin's SOLID principles of Object Oriented Design,
DI
is one of the possible implementations of the Dependency Inversion Principle (DIP). The DIP is theD
of theSOLID
mantra - other DIP implementations include the Service Locator, and Plugin patterns.The objective of the DIP is to decouple tight, concrete dependencies between classes, and instead, to loosen the coupling by means of an abstraction, which can be achieved via an
interface
,abstract class
orpure virtual class
, depending on the language and approach used.Without the DIP, our code (I've called this 'consuming class') is directly coupled to a concrete dependency and is also often burdened with the responsibility of knowing how to obtain, and manage, an instance of this dependency, i.e. conceptually:
Whereas after application of the DIP, the requirement is loosened, and the concern of obtaining and managing the lifespan of the
Foo
dependency has been removed:Why use DIP (and DI)?
Decoupling dependencies between classes in this way allows for easy substitution of these dependency classes with other implementations which also fulfil the prerequisites of the abstraction (e.g. the dependency can be switched with another implementation of the same interface). Moreover, as others have mentioned, possibly the most common reason to decouple classes via the DIP is to allow a consuming class to be tested in isolation, as these same dependencies can now be stubbed and/or mocked.
One consequence of DI is that the lifespan management of dependency object instances is no longer controlled by a consuming class, as the dependency object is now passed into the consuming class (via constructor or setter injection).
This can be viewed in different ways:
Create
on the factory as needed, and dispose of these instances once complete.When to use DI?
MyDepClass
is thread safe - what if we make it a singleton and inject the same instance into all consumers?)Example
Here's a simple C# implementation. Given the below Consuming class:
Although seemingly innocuous, it has two
static
dependencies on two other classes,System.DateTime
andSystem.Console
, which not only limit the logging output options (logging to console will be worthless if no one is watching), but worse, it is difficult to automatically test given the dependency on a non-deterministic system clock.We can however apply
DIP
to this class, by abstracting out the the concern of timestamping as a dependency, and couplingMyLogger
only to a simple interface:We can also loosen the dependency on
Console
to an abstraction, such as aTextWriter
. Dependency Injection is typically implemented as eitherconstructor
injection (passing an abstraction to a dependency as a parameter to the constructor of a consuming class) orSetter Injection
(passing the dependency via asetXyz()
setter or a .Net Property with{set;}
defined). Constructor Injection is preferred, as this guarantees the class will be in a correct state after construction, and allows the internal dependency fields to be marked asreadonly
(C#) orfinal
(Java). So using constructor injection on the above example, this leaves us with:(A concrete
Clock
needs to be provided, which of course could revert toDateTime.Now
, and the two dependencies need to be provided by an IoC container via constructor injection)An automated Unit Test can be built, which definitively proves that our logger is working correctly, as we now have control over the dependencies - the time, and we can spy on the written output:
Next Steps
Dependency injection is invariably associated with an Inversion of Control container(IoC), to inject (provide) the concrete dependency instances, and to manage lifespan instances. During the configuration / bootstrapping process,
IoC
containers allow the following to be defined:IBar
, return aConcreteBar
instance")IDisposable
and will take on the responsibility ofDisposing
dependencies in line with the configured lifespan management.Typically, once IoC containers have been configured / bootstrapped, they operate seamlessly in the background allowing the coder to focus on the code at hand rather than worrying about dependencies.
As per above example, decoupling of dependencies does require some design effort, and for the developer, there is a paradigm shift needed to break the habit of
new
ing dependencies directly, and instead trusting the container to manage dependencies.But the benefits are many, especially in the ability to thoroughly test your class of interest.
Note : The creation / mapping / projection (via
new ..()
) of POCO / POJO / Serialization DTOs / Entity Graphs / Anonymous JSON projections et al - i.e. "Data only" classes or records - used or returned from methods are not regarded as Dependencies (in the UML sense) and not subject to DI. Usingnew
to project these is just fine.依赖注入 (DI) 的全部目的是保持应用程序源代码干净和稳定:
实际上,每个设计模式都会分离关注点以使未来的更改影响最小文件。
DI 的具体领域是依赖配置和初始化的委托。
示例:使用 shell 脚本进行 DI
如果您偶尔在 Java 之外工作,请回想一下
source
在许多脚本语言(Shell、Tcl 等)中的常用方式,甚至是import
Python 被滥用于此目的)。考虑简单的
dependent.sh
脚本:该脚本是相关的:它自己不会成功执行(
archive_files
未定义)。您在
archive_files_zip.sh
实现脚本中定义archive_files
(在本例中使用zip
):而不是
source
-ing直接在依赖项中执行脚本,您使用一个包含两个“组件”的injector.sh
“容器”:archive_files
依赖项刚刚已注入到依赖脚本中。您可以使用
tar
或xz
注入实现archive_files
的依赖项。示例:删除 DI
如果
dependent.sh
脚本直接使用依赖项,则该方法将称为依赖项查找(与依赖项注入相反):现在的问题是依赖的“组件”必须自己执行初始化。
“组件”的源代码既不是干净也不是稳定,因为依赖项初始化的每次更改都需要“组件”源代码文件的新版本。
最后的话
DI 并没有像 Java 框架那样受到重视和普及。
但这是一种通用方法来划分以下方面的关注点:
仅将配置与依赖项查找结合使用并无帮助,因为每个依赖项的配置参数数量(例如新的身份验证类型)以及受支持的依赖项类型的数量(例如新的身份验证类型)可能会发生变化。数据库类型)。
The whole point of Dependency Injection (DI) is to keep application source code clean and stable:
Practically, every design pattern separates concerns to make future changes affect minimum files.
The specific domain of DI is delegation of dependency configuration and initialization.
Example: DI with shell script
If you occasionally work outside of Java, recall how
source
is often used in many scripting languages (Shell, Tcl, etc., or evenimport
in Python misused for this purpose).Consider simple
dependent.sh
script:The script is dependent: it won't execute successfully on its own (
archive_files
is not defined).You define
archive_files
inarchive_files_zip.sh
implementation script (usingzip
in this case):Instead of
source
-ing implementation script directly in the dependent one, you use aninjector.sh
"container" which wraps both "components":The
archive_files
dependency has just been injected into dependent script.You could have injected dependency which implements
archive_files
usingtar
orxz
.Example: removing DI
If
dependent.sh
script used dependencies directly, the approach would be called dependency lookup (which is opposite to dependency injection):Now the problem is that dependent "component" has to perform initialization itself.
The "component"'s source code is neither clean nor stable because every changes in initialization of dependencies requires new release for "components"'s source code file as well.
Last words
DI is not as largely emphasized and popularized as in Java frameworks.
But it's a generic approach to split concerns of:
Using configuration only with dependency lookup does not help as number of configuration parameters may change per dependency (e.g. new authentication type) as well as number of supported types of dependencies (e.g. new database type).
以上答案都很好,我的目的是用简单的方式解释这个概念,以便任何没有编程知识的人也能理解概念
依赖注入是帮助我们创建复杂的设计模式之一系统以更简单的方式。
我们可以在日常生活中看到这种模式的广泛应用。
其中一些示例包括磁带录音机、VCD、CD 驱动器等。
上图是 Reel-to-reel 便携式磁带录音机,20 世纪中期的图像世纪。 来源。
录音机的主要用途是录制或播放声音。
在设计系统时,需要一个卷轴来录制或播放声音或音乐。 设计该系统有两种可能性:
如果我们使用第一个,我们需要打开机器来更换卷轴。
如果我们选择第二种,即为卷轴放置一个钩子,我们将获得通过更换卷轴播放任何音乐的额外好处。 并且还减少了仅播放卷轴中的任何内容的功能。
同样,依赖注入是将依赖关系外部化以仅关注组件的特定功能的过程,以便独立组件可以耦合在一起形成复杂的系统。
我们通过使用依赖注入实现的主要好处。
如今,这些概念构成了编程世界中众所周知的框架的基础。
Spring Angular 等是建立在这个概念之上的众所周知的软件框架
依赖注入是一种模式,用于创建其他对象所依赖的对象实例,而无需在编译时知道哪个类将用于提供该功能或简单地向对象注入属性的方式称为依赖注入。
依赖注入示例
之前我们正在编写这样的代码
通过依赖注入,依赖注入器将为我们取消实例化
您还可以阅读
控制反转与控制反转之间的区别 依赖注入
All the above answers are good, my aim is to explain the concept in a simple way so that anyone without a programming knowledge can also understand concept
Dependency injection is one of the design pattern that help us to create complex systems in a simpler manner.
We can see a wide variety of application of this pattern in our day to day life.
Some of the examples are Tape recorder, VCD, CD Drive etc.
The above image is an image of Reel-to-reel portable tape recorder, mid-20th century. Source.
The primary intention of a tape recorder machine is to record or playback sound.
While designing a system it require a reel to record or playback sound or music. There are two possibilities for designing this system
If we use the first one we need to open the machine to change the reel.
if we opt for the second one, that is placing a hook for reel, we are getting an added benefit of playing any music by changing the reel. and also reducing the function only to playing whatever in the reel.
Like wise dependency injection is the process of externalizing the dependencies to focus only on the specific functionality of the component so that independent components can be coupled together to form a complex system.
The main benefits we achieved by using dependency injection.
Now a days these concept forms the basis of well known frameworks in programming world.
The Spring Angular etc are the well-known software frameworks built on the top of this concept
Dependency injection is a pattern used to create instances of objects that other objects rely upon without knowing at compile time which class will be used to provide that functionality or simply the way of injecting properties to an object is called dependency injection.
Example for Dependency injection
Previously we are writing code like this
With Dependency injection, the dependency injector will take off the instantiation for us
You can also read
Difference between Inversion of Control & Dependency Injection
例如,我们有 2 个类
Client
和Service
。Client
将使用Service
,无需依赖注入
方式 1)
方式 2)
方式 3)
1) 2) 3) 使用
优点
缺点
Client
类时Service
构造函数,我们需要在创建Service
对象的所有地方更改代码使用依赖注入
方式 1) 构造函数注入
使用
方式2) Setter注入
使用
方式3)接口注入
检查https://en.wikipedia.org/wiki/Dependency_injection
===
现在,这段代码已经遵循
依赖注入
并且更容易测试Client
类。然而,我们仍然多次使用
new Service()
,并且当改变Service
构造函数时就不好了。 为了防止这种情况,我们可以使用 DI 注入器,例如1) 简单手册
Injector
使用
2) 使用库:对于 Android dagger2
优点
Service
时,你只需要在Injector类中改变它构造函数注入
>,当你查看Client
的构造函数时,你会看到Client
类有多少依赖缺点
构造函数注入
,Service
对象是在Client
创建时创建的,有时我们使用Client
类中的函数而不使用Service
code> 这样创建的Service
是浪费依赖注入定义
https://en.wikipedia。 org/wiki/Dependency_injection
Example, we have 2 class
Client
andService
.Client
will useService
Without Dependency Injection
Way 1)
Way 2)
Way 3)
1) 2) 3) Using
Advantages
Disadvantages
Client
classService
constructor, we need to change code in all place createService
objectUse Dependency Injection
Way 1) Constructor injection
Using
Way 2) Setter injection
Using
Way 3) Interface injection
Check https://en.wikipedia.org/wiki/Dependency_injection
===
Now, this code is already follow
Dependency Injection
and it is easier for testClient
class.However, we still use
new Service()
many time and it is not good when changeService
constructor. To prevent it, we can use DI injector like1) Simple manual
Injector
Using
2) Use library: For Android dagger2
Advantages
Service
, you only need to change it in Injector classConstructor Injection
, when you look at constructor ofClient
, you will see how many dependency ofClient
classDisadvantages
Constructor Injection
, theService
object is created whenClient
created, sometime we use function inClient
class without useService
so createdService
is wastedDependency Injection definition
https://en.wikipedia.org/wiki/Dependency_injection
什么是依赖注入?
依赖注入(DI)是指将相互依赖的对象解耦。 假设对象 A 依赖于对象 B,因此我们的想法是将这些对象彼此解耦。 我们不需要使用 new 关键字对对象进行硬编码,而是在运行时共享对象的依赖关系,而不管编译时间。
如果我们谈论
依赖注入在 Spring 中的工作原理:
我们不需要使用 new 关键字对对象进行硬编码,而是在配置文件中定义 bean 依赖项。 spring 容器将负责连接所有内容。
控制反转 (IOC)
IOC 是一个通用概念,可以用多种不同的方式表达,依赖注入就是 IOC 的一个具体示例。
依赖注入的两种类型:
1. 基于构造函数的依赖注入:
当容器调用带有多个参数的类构造函数时,基于构造函数的 DI 就完成了,每个参数代表对其他类的依赖。
2. 基于 Setter 的依赖注入:
基于 Setter 的 DI 是通过容器在调用无参数构造函数或无参数静态工厂方法来实例化 bean 后调用 bean 上的 setter 方法来完成的。
笔记:
使用构造函数参数来实现强制依赖项,使用 setter 来实现可选依赖项是一个很好的经验法则。 请注意,如果我们在 setter 上使用基于 @Required 注释的注释,则可以使用 setter 作为必需的依赖项。
What is dependency Injection?
Dependency Injection(DI) means to decouple the objects which are dependent on each other. Say object A is dependent on Object B so the idea is to decouple these object from each other. We don’t need to hard code the object using new keyword rather sharing dependencies to objects at runtime in spite of compile time.
If we talk about
How Dependency Injection works in Spring:
We don’t need to hard code the object using new keyword rather define the bean dependency in the configuration file. The spring container will be responsible for hooking up all.
Inversion of Control (IOC)
IOC is a general concept and it can be expressed in many different ways and Dependency Injection is one concrete example of IOC.
Two types of Dependency Injection:
1. Constructor-based dependency injection:
Constructor-based DI is accomplished when the container invokes a class constructor with a number of arguments, each representing a dependency on other class.
2. Setter-based dependency injection:
Setter-based DI is accomplished by the container calling setter methods on your beans after invoking a no-argument constructor or no-argument static factory method to instantiate your bean.
NOTE:
It is a good rule of thumb to use constructor arguments for mandatory dependencies and setters for optional dependencies. Note that the if we use annotation based than @Required annotation on a setter can be used to make setters as a required dependencies.
这意味着对象应该只具有完成其工作所需的依赖项,并且依赖项应该很少。 此外,如果可能的话,对象的依赖关系应该依赖于接口,而不是“具体”对象。 (具体对象是使用关键字 new 创建的任何对象。)松散耦合促进了更大的可重用性、更容易的可维护性,并允许您轻松地提供“模拟”对象来代替昂贵的服务。
“依赖注入”(DI)也称为“控制反转”(IoC),可以用作促进这种松散耦合的技术。
实现 DI 有两种主要方法:
构造函数注入
这是将对象依赖项传递给其构造函数的技术。
请注意,构造函数接受接口而不是具体对象。 另请注意,如果 orderDao 参数为 null,则会引发异常。 这强调了接收有效依赖的重要性。 在我看来,构造函数注入是为对象提供依赖项的首选机制。 开发人员在调用对象时很清楚需要将哪些依赖项提供给“Person”对象才能正确执行。
Setter 注入
但请考虑以下示例...假设您有一个类,其中有 10 个没有依赖项的方法,但您要添加一个确实依赖于 IDAO 的新方法。 您可以更改构造函数以使用构造函数注入,但这可能会迫使您更改整个地方的所有构造函数调用。 或者,您可以只添加一个接受依赖项的新构造函数,但是开发人员如何轻松知道何时使用一个构造函数而不是另一个构造函数。 最后,如果创建依赖项的成本非常昂贵,那么为什么要创建它并将其传递给构造函数,而它可能很少使用呢? “Setter 注入”是另一种可以在这种情况下使用的 DI 技术。
Setter 注入不会强制将依赖项传递给构造函数。 相反,依赖关系被设置到需要的对象公开的公共属性上。 正如前面所暗示的,这样做的主要动机包括:
以下是上述代码的示例:
It means that objects should only have as many dependencies as is needed to do their job and the dependencies should be few. Furthermore, an object’s dependencies should be on interfaces and not on “concrete” objects, when possible. (A concrete object is any object created with the keyword new.) Loose coupling promotes greater reusability, easier maintainability, and allows you to easily provide “mock” objects in place of expensive services.
The “Dependency Injection” (DI) is also known as “Inversion of Control” (IoC), can be used as a technique for encouraging this loose coupling.
There are two primary approaches to implementing DI:
Constructor injection
It’s the technique of passing objects dependencies to its constructor.
Note that the constructor accepts an interface and not concrete object. Also, note that an exception is thrown if the orderDao parameter is null. This emphasizes the importance of receiving a valid dependency. Constructor Injection is, in my opinion, the preferred mechanism for giving an object its dependencies. It is clear to the developer while invoking the object which dependencies need to be given to the “Person” object for proper execution.
Setter Injection
But consider the following example… Suppose you have a class with ten methods that have no dependencies, but you’re adding a new method that does have a dependency on IDAO. You could change the constructor to use Constructor Injection, but this may force you to changes to all constructor calls all over the place. Alternatively, you could just add a new constructor that takes the dependency, but then how does a developer easily know when to use one constructor over the other. Finally, if the dependency is very expensive to create, why should it be created and passed to the constructor when it may only be used rarely? “Setter Injection” is another DI technique that can be used in situations such as this.
Setter Injection does not force dependencies to be passed to the constructor. Instead, the dependencies are set onto public properties exposed by the object in need. As implied previously, the primary motivators for doing this include:
Here is the example of how the above code would look like:
我能想到的最好的比喻是手术室里的外科医生和他的助手,外科医生是主要人物,他的助手在他需要时提供各种手术部件,以便外科医生可以专注于其中一个他最擅长的事情(手术)。 如果没有助手,外科医生每次需要部件时都必须自己去获取部件。
DI 简称 DI,是一种通过向组件提供依赖组件来消除组件获取依赖组件的常见附加责任(负担)的技术。
DI 让您更接近单一职责 (SR) 原则,就像
外科医生可以专注于手术
。何时使用 DI :我建议在几乎所有生产项目(小/大)中使用 DI,特别是在不断变化的业务环境中:)
原因:因为您希望您的代码易于测试、可模拟等,以便您可以快速测试您的代码改变并推向市场。 此外,当你有很多很棒的免费工具/框架来支持你的代码库之旅时,你为什么不呢?
The best analogy I can think of is the surgeon and his assistant(s) in an operation theater, where the surgeon is the main person and his assistant who provides the various surgical components when he needs it so that the surgeon can concentrate on the one thing he does best (surgery). Without the assistant the surgeon has to get the components himself every time he needs one.
DI for short, is a technique to remove a common additional responsibility (burden) on components to fetch the dependent components, by providing them to it.
DI brings you closer to the Single Responsibility (SR) principle, like the
surgeon who can concentrate on surgery
.When to use DI : I would recommend using DI in almost all production projects ( small/big), particularly in ever changing business environments :)
Why : Because you want your code to be easily testable, mockable etc so that you can quickly test your changes and push it to the market. Besides why would you not when you there are lots of awesome free tools/frameworks to support you in your journey to a codebase where you have more control.
依赖注入意味着代码的一部分(例如类)以模块化方式访问依赖项(代码的其他部分,例如它所依赖的其他类),而无需对它们进行硬编码(因此它们可以自由更改或覆盖,甚至根据需要在其他时间加载)
(还有 PS,是的,它已经成为一个过度炒作的 25 美元名称,一个相当简单的概念),我的
.25
美分Dependency Injection means a way (actually any-way) for one part of code (e.g a class) to have access to dependencies (other parts of code, e.g other classes, it depends upon) in a modular way without them being hardcoded (so they can change or be overriden freely, or even be loaded at another time, as needed)
(and ps , yes it has become an overly-hyped 25$ name for a rather simple, concept), my
.25
cents流行的答案是没有帮助的,因为它们以一种没有用的方式定义依赖注入。 我们一致认为,“依赖”是指我们的对象 X 需要的一些预先存在的其他对象。 ”时,我们并不是说我们正在执行“依赖注入”
但是,当我们说“我们只是调用将参数传递到构造函数中 。 自从构造函数被发明以来,我们就一直定期这样做。
“依赖注入”被认为是一种“控制反转”,这意味着从调用者那里取出了一些逻辑。 当调用者传入参数时情况并非如此,因此如果是 DI,DI 并不意味着控制反转。
DI 意味着调用者和构造函数之间有一个管理依赖关系的中间级别。 Makefile 是依赖注入的一个简单示例。 “调用者”是在命令行中输入“make bar”的人,“构造器”是编译器。 Makefile 指定 bar 依赖于 foo,并且它
在执行 a 之前先执行 a。
输入“make bar”的人不需要知道 bar 依赖于 foo。 依赖关系被注入到“make bar”和 gcc 之间。
中间级别的主要目的不仅仅是将依赖项传递给构造函数,而是将所有依赖项列出在只是一个地方,并对编码器隐藏它们(不是让编码器提供它们)。
通常,中间层为构造的对象提供工厂,它必须提供每个请求的对象类型必须满足的角色。 这是因为通过隐藏构造细节的中间级别,您已经遭受了工厂施加的抽象惩罚,因此您不妨使用工厂。
The popular answers are unhelpful, because they define dependency injection in a way that isn't useful. Let's agree that by "dependency" we mean some pre-existing other object that our object X needs. But we don't say we're doing "dependency injection" when we say
We just call that passing parameters into the constructor. We've been doing that regularly ever since constructors were invented.
"Dependency injection" is considered a type of "inversion of control", which means that some logic is taken out of the caller. That isn't the case when the caller passes in parameters, so if that were DI, DI would not imply inversion of control.
DI means there is an intermediate level between the caller and the constructor which manages dependencies. A Makefile is a simple example of dependency injection. The "caller" is the person typing "make bar" on the command line, and the "constructor" is the compiler. The Makefile specifies that bar depends on foo, and it does a
before doing a
The person typing "make bar" doesn't need to know that bar depends on foo. The dependency was injected between "make bar" and gcc.
The main purpose of the intermediate level is not just to pass in the dependencies to the constructor, but to list all the dependencies in just one place, and to hide them from the coder (not to make the coder provide them).
Usually the intermediate level provides factories for the constructed objects, which must provide a role that each requested object type must satisfy. That's because by having an intermediate level that hides the details of construction, you've already incurred the abstraction penalty imposed by factories, so you might as well use factories.
摘自本书,“基础良好的 Java 开发人员:Java 7 和多语言编程的重要技术
From the Book, 'Well-Grounded Java Developer: Vital techniques of Java 7 and polyglot programming
依赖注入是解决通常被称为“依赖混淆”需求的一种可能的解决方案。 依赖关系混淆是一种从向需要该依赖关系的类提供依赖关系的过程中去除“明显”性质的方法,从而以某种方式混淆向所述类提供所述依赖关系。 这不一定是坏事。 事实上,通过混淆向类提供依赖项的方式,类外部的某些东西负责创建依赖项,这意味着在各种情况下,可以向类提供依赖项的不同实现,而无需进行任何更改到班级。 这对于在生产和测试模式之间切换(例如,使用“模拟”服务依赖项)非常有用。
不幸的是,不好的部分是,有些人认为您需要一个专门的框架来进行依赖项混淆,并且如果您选择不使用特定的框架来执行此操作,那么您在某种程度上是一个“次要”程序员。 许多人相信的另一个极其令人不安的神话是,依赖项注入是实现依赖项混淆的唯一方法。 从历史上看,这显然是 100% 错误的,但你很难说服一些人,有依赖注入的替代方案来满足你的依赖混淆需求。
程序员多年来一直了解依赖关系混淆的要求,并且在依赖关系注入之前和之后已经发展了许多替代解决方案。 有工厂模式,但也有许多使用 ThreadLocal 的选项,其中不需要注入特定实例 - 依赖项被有效地注入到线程中,其优点是使对象可用(通过方便的静态 getter 方法)任何需要它的类,而无需向需要它的类添加注释并设置复杂的 XML“粘合剂”来实现它。 当您的依赖项需要持久性(JPA/JDO 或其他)时,它允许您更轻松地实现“透明持久性”,并且域模型和业务模型类纯粹由 POJO 组成(即没有特定于框架/锁定在注释中)。
Dependency injection is one possible solution to what could generally be termed the "Dependency Obfuscation" requirement. Dependency Obfuscation is a method of taking the 'obvious' nature out of the process of providing a dependency to a class that requires it and therefore obfuscating, in some way, the provision of said dependency to said class. This is not necessarily a bad thing. In fact, by obfuscating the manner by which a dependency is provided to a class then something outside the class is responsible for creating the dependency which means, in various scenarios, a different implementation of the dependency can be supplied to the class without making any changes to the class. This is great for switching between production and testing modes (eg., using a 'mock' service dependency).
Unfortunately the bad part is that some people have assumed you need a specialized framework to do dependency obfuscation and that you are somehow a 'lesser' programmer if you choose not to use a particular framework to do it. Another, extremely disturbing myth, believed by many, is that dependency injection is the only way of achieving dependency obfuscation. This is demonstrably and historically and obviously 100% wrong but you will have trouble convincing some people that there are alternatives to dependency injection for your dependency obfuscation requirements.
Programmers have understood the dependency obfuscation requirement for years and many alternative solutions have evolved both before and after dependency injection was conceived. There are Factory patterns but there are also many options using ThreadLocal where no injection to a particular instance is needed - the dependency is effectively injected into the thread which has the benefit of making the object available (via convenience static getter methods) to any class that requires it without having to add annotations to the classes that require it and set up intricate XML 'glue' to make it happen. When your dependencies are required for persistence (JPA/JDO or whatever) it allows you to achieve 'tranaparent persistence' much easier and with domain model and business model classes made up purely of POJOs (i.e. no framework specific/locked in annotations).
5 岁孩子的依赖注入。
当你自己从冰箱里取出东西时,可能会引起问题。 你可能会把门开着,你可能会得到一些妈妈或爸爸不希望你拥有的东西。 您甚至可能正在寻找我们没有或已经过期的东西。
您应该做的是陈述需求,“我需要在午餐时喝点东西”,然后我们会确保您坐下来吃饭时有东西。
Dependency Injection for 5 year olds.
When you go and get things out of the refrigerator for yourself, you can cause problems. You might leave the door open, you might get something Mommy or Daddy doesn't want you to have. You might be even looking for something we don't even have or which has expired.
What you should be doing is stating a need, "I need something to drink with lunch," and then we will make sure you have something when you sit down to eat.
简而言之,依赖注入(DI)是消除不同对象之间的依赖关系或紧密耦合的方法。 依赖注入为每个对象提供了有凝聚力的行为。
DI 是 Spring 的 IOC 原则的实现,即“不要打电话给我们,我们会打电话给你”。 使用依赖注入程序员不需要使用new关键字创建对象。
对象一旦加载到 Spring 容器中,然后我们就可以在需要时通过使用 getBean(String beanName) 方法从 Spring 容器中获取这些对象来重用它们。
In simple words dependency injection (DI) is the way to remove dependencies or tight coupling between different object. Dependency Injection gives a cohesive behavior to each object.
DI is the implementation of IOC principal of Spring which says "Don't call us we will call you". Using dependency injection programmer doesn't need to create object using the new keyword.
Objects are once loaded in Spring container and then we reuse them whenever we need them by fetching those objects from Spring container using getBean(String beanName) method.
我想既然每个人都为 DI 编写过,让我问几个问题。
这是基于 @Adam N 发布的答案。
为什么PersonService 不再需要担心GroupMembershipService 呢? 您刚刚提到 GroupMembership 有多个它依赖的东西(对象/属性)。 如果 PService 中需要 GMService,则您可以将其作为属性。 无论您是否注入它,您都可以模拟它。 我唯一希望注入的情况是 GMService 有更具体的子类,而您直到运行时才知道这些子类。 然后你想要注入子类。 或者,如果您想将其用作单例或原型。 老实说,配置文件对所有内容进行了硬编码,包括在编译时注入类型(接口)的子类。
编辑
Jose Maria Arranz 对 DI 的精彩评论
< em>DI 通过消除确定依赖方向和编写任何粘合代码的需要来增加内聚性。
错。 依赖项的方向是 XML 形式或作为注释,您的依赖项被编写为 XML 代码和注释。 XML 和注释是源代码。
DI 通过使所有组件模块化(即可替换)并具有明确定义的相互接口来减少耦合。
错误。 您不需要 DI 框架来构建基于接口的模块化代码。
关于可替换:使用非常简单的 .properties 存档和 Class.forName 您可以定义哪些类可以更改。 如果您的代码的任何类都可以更改,那么 Java 不适合您,请使用脚本语言。 顺便说一句:如果不重新编译,注释就无法更改。
在我看来,使用 DI 框架的唯一原因只有一个:减少样板。 有了一个做得好的工厂系统,您可以做与您首选的 DI 框架相同、更受控制、更可预测的事情,DI 框架承诺减少代码(XML 和注释也是源代码)。 问题是,在非常非常简单的情况下(每个类一个实例和类似的情况),这种样板文件的减少是真实存在的,有时在现实世界中选择适当的服务对象并不像将类映射到单例对象那么容易。
I think since everyone has written for DI, let me ask a few questions..
This is based on the answer @Adam N posted.
Why does PersonService no longer have to worry about GroupMembershipService? You just mentioned GroupMembership has multiple things(objects/properties) it depends on. If GMService was required in PService, you'd have it as a property. You can mock that out regardless of whether you injected it or not. The only time I'd like it to be injected is if GMService had more specific child classes, which you wouldn't know until runtime. Then you'd want to inject the subclass. Or if you wanted to use that as either singleton or prototype. To be honest, the configuration file has everything hardcoded as far as what subclass for a type (interface) it is going to inject during compile time.
EDIT
A nice comment by Jose Maria Arranz on DI
DI increases cohesion by removing any need to determine the direction of dependency and write any glue code.
False. The direction of dependencies is in XML form or as annotations, your dependencies are written as XML code and annotations. XML and annotations ARE source code.
DI reduces coupling by making all of your components modular (i.e. replaceable) and have well-defined interfaces to each other.
False. You do not need a DI framework to build a modular code based on interfaces.
About replaceable: with a very simple .properties archive and Class.forName you can define which classes can change. If ANY class of your code can be changed, Java is not for you, use an scripting language. By the way: annotations cannot be changed without recompiling.
In my opinion there is one only reason for DI frameworks: boiler plate reduction. With a well done factory system you can do the same, more controlled and more predictable as your preferred DI framework, DI frameworks promise code reduction (XML and annotations are source code too). The problem is this boiler plate reduction is just real in very very simple cases (one instance-per class and similar), sometimes in the real world picking the appropriated service object is not as easy as mapping a class to a singleton object.
依赖注入(DI)是依赖倒置原则(DIP)实践的一部分,也称为控制反转(IoC)。 基本上,您需要进行 DIP,因为您希望使代码更加模块化和单元可测试,而不仅仅是一个整体系统。 因此,您开始识别可以从类中分离并抽象出来的代码部分。 现在需要从类外部注入抽象的实现。 通常这可以通过构造函数来完成。 因此,您创建一个接受抽象作为参数的构造函数,这称为依赖项注入(通过构造函数)。 有关 DIP、DI 和 IoC 容器的更多说明,您可以阅读 这里
Dependency Injection (DI) is part of Dependency Inversion Principle (DIP) practice, which is also called Inversion of Control (IoC). Basically you need to do DIP because you want to make your code more modular and unit testable, instead of just one monolithic system. So you start identifying parts of the code that can be separated from the class and abstracted away. Now the implementation of the abstraction need to be injected from outside of the class. Normally this can be done via constructor. So you create a constructor that accepts the abstraction as a parameter, and this is called dependency injection (via constructor). For more explanation about DIP, DI, and IoC container you can read Here
依赖注入(DI)是设计模式中的一种,它使用 OOP 的基本特征——一个对象与另一个对象的关系。 虽然继承继承一个对象来执行更复杂和更具体的另一个对象,但关系或关联只是使用属性从一个对象创建指向另一个对象的指针。 DI 的强大之处在于与 OOP 的其他功能(如接口和隐藏代码)相结合。
假设,我们在图书馆有一个客户(订阅者),为了简单起见,他只能借一本书。
书籍界面:
接下来我们可以有多种书籍; 其中一种类型是小说:
现在订阅者可以与这本书关联:
所有三个类都可以隐藏以用于其自己的实现。 现在我们可以使用这段代码进行 DI:
有很多不同的方法可以使用依赖注入。 可以将它与Singleton等结合起来,但基本上它只是通过在另一个对象中创建对象类型的属性来实现关联。
有用性仅在于功能,我们应该一次又一次编写的代码始终为我们准备好并完成。 这就是为什么 DI 与控制反转 (IoC) 如此紧密地结合在一起,这意味着我们的程序将控制权传递给另一个正在运行的模块,该模块将 bean 注入到我们的代码中。 (每个可以注入的对象都可以被签名或视为 Bean。)例如,在 Spring 中,它是通过创建和初始化 ApplicationContext 容器来完成的,它为我们完成了这项工作。 我们只需在代码中创建 Context 并调用 beans 的初始化。 在那一刻注射已经自动完成。
Dependency Injection (DI) is one from Design Patterns, which uses the basic feature of OOP - the relationship in one object with another object. While inheritance inherits one object to do more complex and specific another object, relationship or association simply creates a pointer to another object from one object using attribute. The power of DI is in combination with other features of OOP as are interfaces and hiding code.
Suppose, we have a customer (subscriber) in the library, which can borrow only one book for simplicity.
Interface of book:
Next we can have many kind of books; one of type is fiction:
Now subscriber can have association to the book:
All the three classes can be hidden for it's own implementation. Now we can use this code for DI:
There are many different ways how to use dependency injection. It is possible to combine it with Singleton, etc., but still in basic it is only association realized by creating attribute of object type inside another object.
The usefulness is only and only in feature, that code, which we should write again and again is always prepared and done for us forward. This is why DI so closely binded with Inversion of Control (IoC) which means, that our program passes control another running module, which does injections of beans to our code. (Each object, which can be injected can be signed or considered as a Bean.) For example in Spring it is done by creating and initialization ApplicationContext container, which does this work for us. We simply in our code create the Context and invoke initialization the beans. In that moment injection has been done automatically.
我会对依赖注入提出一个稍微不同的、简短而精确的定义,重点关注主要目标,而不是技术手段(遵循此处):
我们在应用程序中创建的对象(无论我们是否使用 Java、C# 或其他面向对象语言)通常属于两类之一:无状态、静态和全局“服务对象”(模块),以及有状态、动态和本地“数据对象”。
模块图(服务对象图)通常在应用程序启动时创建。 这可以使用容器(例如 Spring)来完成,但也可以通过将参数传递给对象构造函数来手动完成。 两种方法都有其优点和缺点,但在应用程序中使用 DI 绝对不需要框架。
一项要求是服务必须根据其依赖性进行参数化。 这到底意味着什么取决于给定系统中采用的语言和方法。 通常,这采用构造函数参数的形式,但使用 setter 也是一种选择。 这也意味着服务的依赖关系对服务的用户是隐藏的(当调用服务方法时)。
什么时候使用? 我想说,只要应用程序足够大,将逻辑封装到单独的模块中,并在模块之间使用依赖关系图,就可以提高代码的可读性和可探索性。
I would propose a slightly different, short and precise definition of what Dependency Injection is, focusing on the primary goal, not on the technical means (following along from here):
The objects that we create in our applications (regardless if we use Java, C# or other object-oriented language) usually fall into one of two categories: stateless, static and global “service objects” (modules), and stateful, dynamic and local “data objects”.
The module graph - the graph of service objects - is typically created on application startup. This can be done using a container, such as Spring, but can also be done manually, by passing parameters to object constructors. Both ways have their pros and cons, but a framework definitely isn’t necessary to use DI in your application.
One requirement is that the services must be parametrised by their dependencies. What this means exactly depends on the language and approach taken in a given system. Usually, this takes the form of constructor parameters, but using setters is also an option. This also means that the dependencies of a service are hidden (when invoking a service method) from the users of the service.
When to use? I would say whenever the application is large enough that encapsulating logic into separate modules, with a dependency graph between the modules gives a gain in readability and explorability of the code.