什么是dependency injection?
已经发布了几个关于dependency injection的具体问题的问题,例如何时使用它以及它有哪些框架。 然而,
什么是dependency injection,什么时候/为什么应该或不应该被使用?
基本上,不是让对象创build一个依赖关系,或者让一个工厂对象为它们创build一个对象,而是将所需的依赖关系传递给外部对象,并使其成为别人的问题。 这个“某人”或者是依赖关系图的一个对象,或者是一个构build依赖关系图的dependency injection器(框架)。 我在这里使用的依赖关系是当前对象需要引用的任何其他对象。
dependency injection的一个主要优点是它可以使testing更容易。 假设你有一个在构造函数中做了如下的对象:
public SomeClass() { myObject = Factory.getObject(); }
当你所要做的只是在SomeClass上运行一些unit testing时,这可能会很麻烦,特别是如果myObject是复杂的磁盘或networking访问。 所以现在你正在嘲笑myObject,但也以某种方式拦截工厂调用。 硬。 相反,将该对象作为parameter passing给构造函数。 现在你已经把问题转移到其他地方了,但是testing可以变得更容易。 只需制作一个虚拟的myObject并将其传入即可。构造函数现在看起来有点像:
public SomeClass (MyClass myObject) { this.myObject = myObject; }
这是dependency injection的一种风格 – 通过构造函数。 有几种机制是可能的。
- 正如在评论中指出的,一个常见的select是定义一个无所作为的构造函数,并通过属性设置器(h / t @MikeVella)注入依赖项。
- Martin Fowlerlogging了第三种替代方法(h / t @MarcDix),其中类明确实现了他们希望注入的依赖关系的接口。
当不使用dependency injection时(例如,在构造函数中做了太多工作的类),在unit testing中分离组件变得越来越困难。
回到2013年,当我写这个答案时,这是Googletesting博客上的一个主题。 这对我来说仍然是最大的优势,因为在运行时devise中(例如服务定位器或类似模式)可能并不总是需要额外的灵活性,但是在testing过程中,您经常需要能够隔离您的类。
到目前为止,我发现的最好的定义是詹姆斯·肖尔(James Shore)
“dependency injection”是一个5美分的概念25美元的术语。 […]dependency injection意味着给一个对象的实例variables。 […]。
Martin Fowler的一篇文章可能也有用。
dependency injection基本上是提供一个对象需要的对象(它的依赖关系),而不是让它自己构造它们。 这是一个非常有用的testing技术,因为它允许依赖性被嘲弄或被剔除。
依赖关系可以通过多种方式注入对象(如构造器注入或setter注入)。 甚至可以使用专门的dependency injection框架(如Spring)来做到这一点,但它们当然不是必需的。 你不需要这些框架来进行dependency injection。 显式实例化和传递对象(依赖关系)与框架注入一样好。
我发现这个有趣的例子松耦合方面:
任何应用程序都由许多对象组成,彼此协作执行一些有用的东西。 传统上,每个对象都负责获取自己对其协作的依赖对象(依赖关系)的引用。 这导致高度耦合的类和难以testing的代码。
例如,考虑一个Car
对象。
Car
取决于车轮,发动机,燃料,电池等运行。 传统上,我们定义了这些依赖对象的品牌以及Car
对象的定义。
无dependency injection(DI):
class Car{ private Wheel wh= new NepaliRubberWheel(); private Battery bt= new ExcideBattery(); //The rest }
在这里, Car
对象负责创build依赖对象。
如果我们想要在最初的NepaliRubberWheel()
戳穿之后改变它的依赖对象的types,比如Wheel
,那该怎么办呢? 我们需要重新创buildCar对象的新依赖关系,比如ChineseRubberWheel()
,但是只有Car
制造商可以做到这一点。
那么Dependency Injection
为我们做了什么?
当使用dependency injection时,对象在运行时被赋予它们的依赖关系,而不是编译时间(汽车制造时间) 。 所以我们现在可以随时改变Wheel
。 在这里, dependency
( wheel
)可以在运行时注入Car
。
使用dependency injection之后:
在这里,我们在运行时注入 依赖关系 (Wheel和Battery)。 因此术语: dependency injection。
class Car{ private Wheel wh= [Inject an Instance of Wheel (dependency of car) at runtime] private Battery bt= [Inject an Instance of Battery (dependency of car) at runtime] Car(Wheel wh,Battery bt) { this.wh = wh; this.bt = bt; } //Or we can have setters void setWheel(Wheel wh) { this.wh = wh; } }
来源: 了解dependency injection
dependency injection是一种实践,其中的对象被devise为从其他代码片段接收对象的实例,而不是在内部构造它们。 这意味着任何实现对象所需接口的对象都可以在不改变代码的情况下进行replace,简化了testing,提高了解耦性。
例如,考虑这些类:
public class PersonService { public void addManager( Person employee, Person newManager ) { ... } public void removeManager( Person employee, Person oldManager ) { ... } public Group getGroupByManager( Person manager ) { ... } } public class GroupMembershipService() { public void addPersonToGroup( Person person, Group group ) { ... } public void removePersonFromGroup( Person person, Group group ) { ... } }
在这个例子中, PersonService::addManager
和PersonService::removeManager
需要一个PersonService::removeManager
的实例来完成它的工作。 没有dependency injection,传统的做法是在PersonService
的构造函数中实例化一个新的PersonService
并在两个函数中使用该实例属性。 但是,如果GroupMembershipService
的构造函数有多个要求,或者更糟糕的是,有一些需要在GroupMembershipService
上调用的初始化“设置者”,代码增长得相当快,而且PersonService
现在不仅取决于GroupMembershipService
,也是GroupMembershipService
依赖的其他一切。 此外,与GroupMembershipService
的链接被硬编码到PersonService
,这意味着您不能为testing目的而“ GroupMembershipService
” GroupMembershipService
,或者在应用程序的不同部分使用策略模式。
使用dependency injection,而不是在PersonService
中实例化PersonService
,而是将其传递给PersonService
构造函数,或者添加一个Property(getter和setter)来设置它的本地实例。 这意味着你的PersonService
不再需要担心如何创build一个GroupMembershipService
,它只是接受它所给予的,并与他们一起工作。 这也意味着GroupMembershipService
的子类或实现GroupMembershipService
接口的任何东西都可以被“注入”到PersonService
,而PersonService
不需要知道这个改变。
接受的答案是一个很好的答案 – 但我想补充说,DI非常像代码中经典的避免硬编码常量。
当你使用像数据库名称这样的常量时,你可以快速地将它从代码内部移动到某个configuration文件中,并将包含该值的variables传递到需要的地方。 这样做的原因是这些常量的变化通常比其他代码更频繁。 例如,如果您想在testing数据库中testing代码。
DI在面向对象编程领域与此类似。 那里的值而不是常量字面量是整个对象 – 但是将代码从类代码中移出的原因是相似的 – 对象更改的频率比使用它们的代码更频繁。 一个重要的情况下需要这样的变化是testing。
假设你想去钓鱼:
-
没有dependency injection,你需要自己照顾一切。 你需要找一条船,买钓竿,寻找诱饵等等。当然,这是可能的,但是这会给你带来很大的责任。 用软件来说,这意味着你必须对所有这些东西进行查找。
-
通过dependency injection,其他人将负责所有的准备工作,并为您提供所需的设备。 你会收到(“被注入”)船,钓鱼竿和诱饵 – 都准备好使用。
这是我见过的关于dependency injection和dependency injection容器的最简单的解释:
没有dependency injection
- 应用程序需要Foo(例如一个控制器),所以:
- 应用程序创buildFoo
- 应用程序调用Foo
- Foo需要Bar(例如服务),所以:
- Foo创buildBar
- Foo打电话给Bar
- Bar需要Bim(一个服务,一个仓库…),所以:
- 酒吧创buildBim
- 酒吧做一些事情
dependency injection
- 应用程序需要Foo,需要Bar,需要Bim,所以:
- 应用程序创buildBim
- 应用程序创build吧,并给它BIM
- 应用程序创buildFoo并给它吧
- 应用程序调用Foo
- Foo打电话给Bar
- 酒吧做一些事情
- Foo打电话给Bar
使用dependency injection容器
- 应用程序需要Foo所以:
- 应用程序从容器获取Foo,所以:
- 容器创buildBim
- 容器创build酒吧,并给它BIM
- 容器创buildFoo并给它吧
- 应用程序调用Foo
- Foo打电话给Bar
- 酒吧做一些事情
- Foo打电话给Bar
dependency injection和dependency injection容器是不同的东西:
- dependency injection是一种编写更好的代码的方法
- DI容器是帮助注入依赖的工具
您不需要容器来执行dependency injection。 然而一个容器可以帮助你。
不是“dependency injection”只是使用参数化的构造函数和公共setter?
詹姆斯·肖尔的文章展示了以下用于比较的例子 。
没有dependency injection的构造函数:
public class Example { private DatabaseThingie myDatabase; public Example() { myDatabase = new DatabaseThingie(); } public void doStuff() { ... myDatabase.getData(); ... } }
具有dependency injection的构造函数:
public class Example { private DatabaseThingie myDatabase; public Example(DatabaseThingie useThisDatabaseInstead) { myDatabase = useThisDatabaseInstead; } public void doStuff() { ... myDatabase.getData(); ... } }
让我们尝试一下Car和Engine类的简单例子,任何汽车都需要一个引擎去任何地方,至less现在是这样。 所以下面的代码看起来没有dependency injection。
public class Car { public Car() { GasEngine engine = new GasEngine(); engine.Start(); } } public class GasEngine { public void Start() { Console.WriteLine("I use gas as my fuel!"); } }
为了实例化Car类,我们将使用下面的代码:
Car car = new Car();
与GasEngine紧密耦合的代码问题,如果我们决定将其更改为ElectricityEngine,那么我们将需要重写Car类。 而越大的应用程序越多的问题和头痛,我们将不得不添加和使用新型引擎。
换句话说,这种方法就是我们的高级Car类依赖于较低级别的GasEngine类,这违反了SOLID中的依赖倒置原则(DIP)。 DIPbuild议我们应该依靠抽象而不是具体的类。 所以为了满足这个需求,我们引入IEngine接口并重写下面的代码:
public interface IEngine { void Start(); } public class GasEngine : IEngine { public void Start() { Console.WriteLine("I use gas as my fuel!"); } } public class ElectricityEngine : IEngine { public void Start() { Console.WriteLine("I am electrocar"); } } public class Car { private readonly IEngine _engine; public Car(IEngine engine) { _engine = engine; } public void Run() { _engine.Start(); } }
现在我们的Car类只依赖于IEngine接口,而不是引擎的具体实现。 现在唯一的诀窍是我们如何创buildCar的一个实例,并给它一个像GasEngine或ElectricityEngine这样的具体引擎类。 这就是dependency injection进来的地方。
Car gasCar = new Car(new GasEngine()); gasCar.Run(); Car electroCar = new Car(new ElectricityEngine()); electroCar.Run();
在这里,我们基本上将我们的依赖项(引擎实例)注入(传递)给Car构造函数。 所以现在我们的类在对象和依赖关系之间有了松散的耦合,我们可以轻松地添加新types的引擎而不需要改变Car类。
dependency injection的主要好处是类更松散耦合,因为它们没有硬编码的依赖关系。 这遵循上面提到的依赖倒置原则。 而不是引用特定的实现,类请求抽象(通常是接口 ),当构造类时提供给它们。
所以最后dependency injection只是一种实现对象及其依赖之间松耦合的技术。 不是直接实例化类需要执行的依赖,而是通过构造函数注入为类提供依赖(通常是)。
另外,当我们有很多依赖时,使用控制反转(IoC)容器是非常好的做法,我们可以知道哪些接口应该映射到所有依赖关系的具体实现,我们可以在构build时解决这些依赖关系我们的对象。 例如,我们可以在IoC容器的映射中指定IEngine依赖关系应该映射到GasEngine类,并且当我们向IoC容器请求一个我们的Car类的实例时,它将自动构造我们的具有GasEngine依赖项的Car类通过了。
更新:最近从Julie Lerman观看有关EF Core的课程,也喜欢她关于DI的简短定义。
dependency injection是一种模式,允许您的应用程序即时注入对象到需要它们的类,而不会强迫这些类负责这些对象。 它允许你的代码更加松散地耦合,Entity Framework Core插入到这个相同的服务系统中。
什么是dependency injection(DI)?
正如其他人所说的, dependency injection(DI)消除了我们感兴趣的类(消费者类)所依赖的其他对象实例(在UML意义上 )的直接创build和pipe理寿命的责任。 这些实例被传递给我们的消费者类,通常作为构造函数参数或通过属性设置器(依赖对象实例化和传递给消费者类的pipe理通常由控制反转(IoC)容器执行,但是这是另一个主题) 。
DI,DIP和SOLID
具体而言,在Robert C Martin的面向对象devise的SOLID原则的范例中, DI
是依赖倒置原则(DIP)的可能实现之一。 DIP是SOLID
咒语 – 其他DIP实现包括服务定位器和插件模式。
DIP的目标是解耦紧密的,具体的类之间的依赖关系,而是通过一个抽象的方式来放松耦合,这可以通过一个interface
, abstract class
或pure virtual class
,取决于使用的语言和方法。
如果没有DIP,我们的代码(我称之为“消费类”)直接与具体的依赖关系相耦合,并且还常常承担着知道如何获取和pipe理这种依赖关系的实例的责任,即在概念上:
"I need to create/use a Foo and invoke method `GetBar()`"
而在DIP的应用之后,要求被放宽了,并且取消和pipe理Foo
依赖的寿命的关心被去除了:
"I need to invoke something which offers `GetBar()`"
为什么使用DIP(和DI)?
以这种方式在类之间解耦依赖关系允许将这些依赖类与其他也满足抽象的先决条件(例如依赖性可以用相同接口的另一个实现方式切换)的实现轻松地replace 。 而且,正如其他人所提到的,通过DIP来分离类的最常见的原因可能是允许对一个消费类进行孤立testing,因为这些相同的依赖关系现在可以被扼杀和/或嘲笑。
DI的一个后果是依赖对象实例的生命周期pipe理不再受消费类的控制,因为依赖对象现在被传递给了消费类(通过构造函数或者setter注入)。
这可以用不同的方式来看待:
- 如果消费类的依赖关系的生命周期控制需要保留,那么可以通过在消费者类中注入一个用于创build依赖类实例的(抽象)工厂来重新build立控制。 消费者将能够根据需要通过工厂上的
Create
获取实例,并在完成后处理这些实例。 - 或者,依赖实例的生命周期控制可以放弃到一个IoC容器(更多关于下面的内容)。
何时使用DI?
- 如果可能需要用一个等同的实施替代一个依赖,
- 任何时候你需要单独testing一个类的方法来隔离它的依赖关系,
- 如果依赖项的生命周期的不确定性可能需要实验(例如,嗨,
MyDepClass
是线程安全的 – 如果我们使它成为一个单例并向所有消费者注入相同的实例,会怎么样?)
例
这是一个简单的C#实现。 鉴于下面的消费类:
public class MyLogger { public void LogRecord(string somethingToLog) { Console.WriteLine("{0:HH:mm:ss} - {1}", DateTime.Now, somethingToLog); } }
虽然看起来无害,但它有两个static
依赖关系的其他两个类, System.DateTime
和System.Console
,这不仅限制日志logging输出选项(如果没有人正在观看,login到控制台将是毫无价值的),但更糟的是,这是困难的自动testing给定依赖于非确定性系统时钟。
然而,我们可以通过将时间戳的关注抽象为依赖关系,并将MyLogger
仅耦合到一个简单的接口来将DIP
应用于此类:
public interface IClock { DateTime Now { get; } }
我们也可以放松Console
对抽象的依赖,比如TextWriter
。 dependency injection通常作为constructor
注入(将抽象传递给依赖作为消费类的构造函数的参数)或Setter Injection
(通过setXyz()
setter或具有{set;}
的.Net属性传递依赖定义)。 构造器注入是首选,因为这保证了类将在构build之后处于正确的状态,并允许将内部依赖项字段标记为readonly
(C#)或final
(Java)。 因此,在上面的例子中使用构造器注入,这给我们留下了:
public class MyLogger : ILogger // Others will depend on our logger. { private readonly TextWriter _output; private readonly IClock _clock; // Dependencies are injected through the constructor public MyLogger(TextWriter stream, IClock clock) { _output = stream; _clock = clock; } public void LogRecord(string somethingToLog) { // We can now use our dependencies through the abstraction // and without knowledge of the lifespans of the dependencies _output.Write("{0:yyyy-MM-dd HH:mm:ss} - {1}", _clock.Now, somethingToLog); } }
(需要提供一个具体的Clock
,当然可以恢复到DateTime.Now
,而这两个依赖需要通过构造器注入由IoC容器提供)
可以build立一个自动化的unit testing,它可以明确certificate我们的logging器工作正常,因为我们现在可以控制依赖关系 – 时间,我们可以监视输出:
[Test] public void LoggingMustRecordAllInformationAndStampTheTime() { // Arrange var mockClock = new Mock<IClock>(); mockClock.Setup(c => c.Now).Returns(new DateTime(2015, 4, 11, 12, 31, 45)); var fakeConsole = new StringWriter(); // Act new MyLogger(fakeConsole, mockClock.Object) .LogRecord("Foo"); // Assert Assert.AreEqual("2015-04-11 12:31:45 - Foo", fakeConsole.ToString()); }
下一步
dependency injection总是与控制反转容器(IoC)相关联,注入(提供)具体依赖实例,并pipe理生命周期实例。 在configuration/引导过程中, IoC
容器允许定义以下内容:
- 每个抽象和configuration的具体实现之间的映射(例如, “任何时候消费者请求一个
IBar
,返回一个ConcreteBar
实例” ) - 可以为每个依赖关系的生命周期pipe理设置策略,例如为每个消费者实例创build一个新对象,跨所有消费者共享一个单例依赖实例,仅在同一个线程中共享相同的依赖实例等。
- 在.Net中,IoC容器知道诸如
IDisposable
之类的协议,并将承担与configuration的生命周期pipe理一致的Disposing
依赖关系的责任。
通常情况下,一旦IoC容器被configuration/引导,它们将在后台无缝地运行,使编码人员能够专注于手头的代码,而不必担心依赖关系。
DI友好代码的关键是避免类的静态耦合,而不是使用new()来创buildDependencies
根据上面的例子,依赖关系的解耦确实需要一些devise工作,而对于开发者来说,需要直接打破new
依赖关系的习惯,而相信容器来pipe理依赖关系。
但好处很多,特别是能够彻底testing你的兴趣class。
注意 :POCO / POJO /序列化DTO /实体图/匿名JSON投影的创build/映射/投影(通过new ..()
) 不会被视为使用或从方法返回的“仅数据”类或logging作为依赖关系(在UML意义上)而不受DI限制。 用new
来投影这些就好了。
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). 所以,
Switch -> PermanentBulb //switch is directly connected to permanent bulb, testing not possible easily
Switch(){ PermanentBulb = new Bulb(); PermanentBulb.Toggle(); }
With Dependency Injection
Switch only knows I need to turn on/off whichever Bulb is passed to me. 所以,
Switch -> Bulb1 OR Bulb2 OR NightBulb (injected dependency)
Switch(AnyBulb){ //pass it whichever bulb you like AnyBulb.Toggle(); }
Modifying James Example for Switch and Bulb:
public class SwitchTest { TestToggleBulb() { MockBulb mockbulb = new MockBulb(); // MockBulb is a subclass of Bulb, so we can // "inject" it here: Switch switch = new Switch(mockBulb); switch.ToggleBulb(); mockBulb.AssertToggleWasCalled(); } } public class Switch { private Bulb myBulb; public Switch() { myBulb = new Bulb(); } public Switch(Bulb useThisBulbInstead) { myBulb = useThisBulbInstead; } public void ToggleBulb() { ... myBulb.Toggle(); ... } }`
The whole point of Dependency Injection (DI) is to keep application source code clean and stable :
- clean of dependency initialization code
- stable regardless of dependency used
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 even import
in Python misused for this purpose).
Consider simple dependent.sh
script:
#!/bin/sh # Dependent touch "one.txt" "two.txt" archive_files "one.txt" "two.txt"
The script is dependent: it won't execute successfully on its own ( archive_files
is not defined).
You define archive_files
in archive_files_zip.sh
implementation script (using zip
in this case):
#!/bin/sh # Dependency function archive_files { zip files.zip "$@" }
Instead of source
-ing implementation script directly in the dependent one, you use an injector.sh
"container" which wraps both "components":
#!/bin/sh # Injector source ./archive_files_zip.sh source ./dependent.sh
The archive_files
dependency has just been injected into dependent script.
You could have injected dependency which implements archive_files
using tar
or xz
.
Example: removing DI
If dependent.sh
script used dependencies directly, the approach would be called dependency lookup (which is opposite to dependency injection ):
#!/bin/sh # Dependent # dependency look-up source ./archive_files_zip.sh touch "one.txt" "two.txt" archive_files "one.txt" "two.txt"
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.
最后的话
DI is not as largely emphasized and popularized as in Java frameworks.
But it's a generic approach to split concerns of:
- application development ( single source code release lifecycle)
- application deployment ( multiple target environments with independent lifecycles)
Using configuration only with dependency lookup does not help as number of configuration parameters may change per dependency (eg new authentication type) as well as number of supported types of dependencies (eg new database type).
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:
- Constructor Injection
- Setter 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.
public class Triangle { private String type; public String getType(){ return type; } public Triangle(String type){ //constructor injection this.type=type; } } <bean id=triangle" class ="com.test.dependencyInjection.Triangle"> <constructor-arg value="20"/> </bean>
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.
public class Triangle{ private String type; public String getType(){ return type; } public void setType(String type){ //setter injection this.type = type; } } <!-- setter injection --> <bean id="triangle" class="com.test.dependencyInjection.Triangle"> <property name="type" value="equivialteral"/>
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.
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.
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
- Setter injection
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:
- Supporting dependency injection without having to modify the constructor of a legacy class.
- Allowing expensive resources or services to be created as late as possible and only when needed.
Here is the example of how the above code would look like:
public class Person { public Person() {} public IDAO Address { set { addressdao = value; } get { if (addressdao == null) throw new MemberAccessException("addressdao" + " has not been initialized"); return addressdao; } } public Address GetAddress() { // ... code that uses the addressdao object // to fetch address details from the datasource ... } // Should not be called directly; // use the public property instead private IDAO addressdao;
I think since everyone has written for DI, let me ask a few questions..
- When you have a configuration of DI where all the actual implementations(not interfaces) that are going to be injected into a class (for eg services to a controller) why is that not some sort of hard-coding?
- What if I want to change the object at runtime? For example, my config already says when I instantiate MyController, inject for FileLogger as ILogger. But I might want to inject DatabaseLogger.
- Everytime I want to change what objects my AClass needs, I need to now look into two places – The class itself and the configuration file. How does that make life easier?
- If Aproperty of AClass is not injected, is it harder to mock it out?
- Going back to the first question. If using new object() is bad, how come we inject the implementation and not the interface? I think a lot of you are saying we're in fact injecting the interface but the configuration makes you specify the implementation of that interface ..not at runtime .. it is hardcoded during compile time.
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.
编辑
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.
假。 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 (ie replacable) and have well-defined interfaces to each other.
假。 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 wich 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.
Dependency Injection means a way (actually any-way ) for one part of code (eg a class) to have access to dependencies (other parts of code, eg 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
I know there are already many answers, but I found this very helpful: http://tutorials.jenkov.com/dependency-injection/index.html
No Dependency:
public class MyDao { protected DataSource dataSource = new DataSourceImpl("driver", "url", "user", "password"); //data access methods... public Person readPerson(int primaryKey) {...} }
Dependency:
public class MyDao { protected DataSource dataSource = null; public MyDao(String driver, String url, String user, String password){ this.dataSource = new DataSourceImpl(driver, url, user, password); } //data access methods... public Person readPerson(int primaryKey) {...} }
Notice how the DataSourceImpl
instantiation is moved into a constructor. The constructor takes four parameters which are the four values needed by the DataSourceImpl
. Though the MyDao
class still depends on these four values, it no longer satisfies these dependencies itself. They are provided by whatever class creating a MyDao
instance.
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
$foo = Foo->new($bar);
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
gcc -c foo.cpp; gcc -c bar.cpp
before doing a
gcc foo.o bar.o -o bar
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.
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 (ie no framework specific/locked in annotations).
From the Book, ' Well-Grounded Java Developer: Vital techniques of Java 7 and polyglot programming
DI is a particular form of IoC, whereby the process of finding your dependencies is outside the direct control of your currently executing code.
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:
package com.deepam.hidden; public interface BookInterface { public BookInterface setHeight(int height); public BookInterface setPages(int pages); public int getHeight(); public int getPages(); public String toString(); }
Next we can have many kind of books; one of type is fiction:
package com.deepam.hidden; public class FictionBook implements BookInterface { int height = 0; // height in cm int pages = 0; // number of pages /** constructor */ public FictionBook() { // TODO Auto-generated constructor stub } @Override public FictionBook setHeight(int height) { this.height = height; return this; } @Override public FictionBook setPages(int pages) { this.pages = pages; return this; } @Override public int getHeight() { // TODO Auto-generated method stub return height; } @Override public int getPages() { // TODO Auto-generated method stub return pages; } @Override public String toString(){ return ("height: " + height + ", " + "pages: " + pages); } }
Now subscriber can have association to the book:
package com.deepam.hidden; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class Subscriber { BookInterface book; /** constructor*/ public Subscriber() { // TODO Auto-generated constructor stub } // injection I public void setBook(BookInterface book) { this.book = book; } // injection II public BookInterface setBook(String bookName) { try { Class<?> cl = Class.forName(bookName); Constructor<?> constructor = cl.getConstructor(); // use it for parameters in constructor BookInterface book = (BookInterface) constructor.newInstance(); //book = (BookInterface) Class.forName(bookName).newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return book; } public BookInterface getBook() { return book; } public static void main(String[] args) { } }
All the three classes can be hidden for it's own implementation. Now we can use this code for DI:
package com.deepam.implement; import com.deepam.hidden.Subscriber; import com.deepam.hidden.FictionBook; public class CallHiddenImplBook { public CallHiddenImplBook() { // TODO Auto-generated constructor stub } public void doIt() { Subscriber ab = new Subscriber(); // injection I FictionBook bookI = new FictionBook(); bookI.setHeight(30); // cm bookI.setPages(250); ab.setBook(bookI); // inject System.out.println("injection I " + ab.getBook().toString()); // injection II FictionBook bookII = ((FictionBook) ab.setBook("com.deepam.hidden.FictionBook")).setHeight(5).setPages(108); // inject and set System.out.println("injection II " + ab.getBook().toString()); } public static void main(String[] args) { CallHiddenImplBook kh = new CallHiddenImplBook(); kh.doIt(); } }
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.
from Book Apress.Spring.Persistence.with.Hibernate.Oct.2010
The purpose of dependency injection is to decouple the work of resolving external software components from your application business logic.Without dependency injection, the details of how a component accesses required services can get muddled in with the component's code. This not only increases the potential for errors, adds code bloat, and magnifies maintenance complexities; it couples components together more closely, making it difficult to modify dependencies when refactoring or testing.
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.
Dependency injection is the heart of the concept related with Spring Framework.While creating the framework of any project spring may perform a vital role,and here dependency injection come in pitcher.
Actually,Suppose in java you created two different classes as class A and class B, and whatever the function are available in class B you want to use in class A, So at that time dependency injection can be used. where you can crate object of one class in other,in the same way you can inject an entire class in another class to make it accessible. by this way dependency can be overcome.
DEPENDENCY INJECTION IS SIMPLY GLUING TWO CLASSES AND AT THE SAME TIME KEEPING THEM SEPARATE.
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
Dependency Injection is a type of implementation of the " Inversion of Control " principle on which is based Frameworks building.
Frameworks as stated in "Design Pattern" of GoF are classes that implement the main control flow logic raising the developer to do that, in this way Frameworks realize the inversion of control principle.
A way to implement as a technique, and not as class hierarchy, this IoC principle it is just Dependency Injection.
DI consists mainly into delegate the mapping of classes instances and type reference to that instances, to an external "entity": an object, static class, component, framework, etc…
Classes instances are the " dependencies ", the external binding of the calling component with the class instance through the reference it is the " injection ".
Obviously you can implement this technique in many way as you want from OOP point of view, see for example constructor injection , setter injection , interface injection .
Delegating a third party to carry out the task of match a ref to an object it is very useful when you want to completely separate a component that needs some services from the same services implementation.
In this way, when designing components, you can focus exclusively on their architecture and their specific logic, trusting on interfaces for collaborating with other objects without worry about any type of implementation changes of objects/services used, also if the same object you are using will be totally replaced (obviously respecting the interface).
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. 来源 。
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. we can place the reel inside the machine or we can provide a hook for the reel where it can be placed.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 playing whatever in the reel.
The main benefits we achieved by using dependency injection.
- High cohesion and loose coupling.
- Externalizing dependency and looking only on responsibility.
- Making things as components and to combine to form a large systems with high capabilities.
- It helps to develop high quality components since they are independently developed they are properly tested.
- It helps to replace the component with another if one fails.
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
Public MyClass{ DependentClass dependentObject /* At somewhere in our code we need to instantiate the object with new operator inorder to use it or perform some method. */ dependentObject= new DependentClass(); dependentObject.someMethod(); }
With Dependency injection, the dependency injector will take off the instantiation for us
Public MyClass{ /* Dependency injector will instantiate object*/ DependentClass dependentObject /* At somewhere in our code we perform some method. The process of instantiation will be handled by the dependency injector */ dependentObject.someMethod(); }
Any nontrivial application is made up of two or more classes that collaborate with each other to perform some business logic. Traditionally, each object is responsible for obtaining its own references to the objects it collaborates with (its dependencies). When applying DI, the objects are given their dependencies at creation time by some external entity that coordinates each object in the system. In other words, dependencies are injected into objects.
For further details please see enter link description here
Here is a great example explanation of dependency injections:
Guice docs