已经发布了几个关于依赖注入的具体问题,例如何时使用它以及它有什么框架,
什么是依赖注入,何时/为什么应该或不应该使用它?
已经发布了几个关于依赖注入的具体问题,例如何时使用它以及它有什么框架,
什么是依赖注入,何时/为什么应该或不应该使用它?
当前回答
我将提出一个稍微不同的、简短而精确的依赖注入定义,侧重于主要目标,而不是技术手段(从这里开始):
依赖注入是创建静态、无状态的服务对象图,其中每个服务由其依赖关系。
我们在应用程序中创建的对象(无论我们是否使用Java、C#或其他面向对象语言)通常分为两类:无状态、静态和全局“服务对象”(模块),以及有状态、动态和本地“数据对象”。
模块图(服务对象图)通常在应用程序启动时创建。这可以使用容器(如Spring)完成,但也可以通过向对象构造函数传递参数来手动完成。这两种方法都有其优点和缺点,但在应用程序中使用DI肯定不需要框架。
一个要求是服务必须通过其依赖性进行参数化。这意味着什么完全取决于给定系统中采用的语言和方法。通常,这采用构造函数参数的形式,但使用setter也是一种选择。这也意味着(在调用服务方法时)对服务的用户隐藏服务的依赖关系。
何时使用?我会说,每当应用程序足够大时,将逻辑封装到单独的模块中,在模块之间使用依赖关系图,可以提高代码的可读性和可探索性。
其他回答
到目前为止,我找到的最好的定义是詹姆斯·肖尔的定义:
“依赖注入”是25美元5美分概念的术语。[...]依赖注入意味着对象的实例变量。[...].
马丁·福勒的一篇文章可能也很有用。
依赖注入基本上是提供对象所需的对象(其依赖项),而不是让它自己构造它们。这是一种非常有用的测试技术,因为它允许对依赖项进行嘲笑或清除。
依赖关系可以通过多种方式注入到对象中(例如构造函数注入或setter注入)。甚至可以使用专门的依赖注入框架(例如Spring)来实现这一点,但它们肯定不是必需的。您不需要这些框架进行依赖注入。显式实例化和传递对象(依赖项)与框架注入一样好。
使依赖注入概念易于理解。让我们以开关按钮为例来切换(打开/关闭)灯泡。
无依赖注入
Switch需要事先知道我连接到哪个灯泡(硬编码依赖项)。所以
开关->永久灯泡//开关直接连接到永久灯泡,测试不容易
Switch(){
PermanentBulb = new Bulb();
PermanentBulb.Toggle();
}
使用依赖注入
开关只知道我需要打开/关闭传递给我的灯泡。所以,
开关->灯泡1或灯泡2或夜灯泡(注入依赖性)
Switch(AnyBulb){ //pass it whichever bulb you like
AnyBulb.Toggle();
}
修改开关和灯泡的James示例:
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();
...
}
}`
针对5岁儿童的依赖注入。
当你自己去把冰箱里的东西拿出来时,你可能会引起问题。你可能会让门开着,你可能会得到妈妈或爸爸不希望你拥有的东西。你甚至可能在寻找我们甚至没有或已经过期的东西。
你应该做的是陈述一个需要,“我需要午餐时喝点东西”,然后我们会确保你坐下吃饭时有东西。
我知道已经有很多答案,但我发现这非常有用:http://tutorials.jenkov.com/dependency-injection/index.html
无相关性:
public class MyDao {
protected DataSource dataSource = new DataSourceImpl(
"driver", "url", "user", "password");
//data access methods...
public Person readPerson(int primaryKey) {...}
}
附属国:
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) {...}
}
注意DataSourceImpl实例化是如何移动到构造函数中的。构造函数接受四个参数,即DataSourceImpl所需的四个值。虽然MyDao类仍然依赖于这四个值,但它本身不再满足这些依赖关系。它们由创建MyDao实例的任何类提供。
“依赖注入”不就是指使用参数化构造函数和公共setter吗?
James Shore的文章展示了以下示例进行比较。
没有依赖注入的构造函数:公共类示例{私有数据库Thingie myDatabase;public Example(){myDatabase=新数据库Thingie();} public void doStuff(){... myDatabase.getData();... } } 具有依赖注入的构造函数:公共类示例{私有数据库Thingie myDatabase;公共示例(DatabaseThingie使用ThisDatabaseReplace){myDatabase=改用此数据库;}public void doStuff(){... myDatabase.getData();... } }