已经发布了几个关于依赖注入的具体问题,例如何时使用它以及它有什么框架,
什么是依赖注入,何时/为什么应该或不应该使用它?
已经发布了几个关于依赖注入的具体问题,例如何时使用它以及它有什么框架,
什么是依赖注入,何时/为什么应该或不应该使用它?
当前回答
我在松耦合方面发现了一个有趣的例子:
来源:了解依赖注入
任何应用程序都由许多对象组成,这些对象相互协作以执行一些有用的任务。传统上,每个对象都负责获取自己对与其协作的依赖对象(依赖关系)的引用。这导致了高度耦合的类和难以测试的代码。
例如,考虑Car对象。
汽车依靠轮子、发动机、燃料、电池等运转。传统上,我们定义此类依赖对象的品牌以及Car对象的定义。
无依赖注入(DI):
class Car{
private Wheel wh = new NepaliRubberWheel();
private Battery bt = new ExcideBattery();
//The rest
}
在这里,Car对象负责创建从属对象。
如果我们希望在初始NepaliRubberWheel()穿孔后更改其从属对象的类型(例如Wheel),该怎么办?我们需要重新创建Car对象及其新的依赖项,例如ChineseRubberWheel(),但只有Car制造商才能做到这一点。
那么依赖注入为我们做了什么。。。?
当使用依赖注入时,对象在运行时而不是编译时(汽车制造时)被赋予依赖性。因此,我们现在可以随时更改轮子。在这里,相关性(轮子)可以在运行时注入Car。
使用依赖注入后:
这里,我们在运行时注入依赖项(Wheel和Battery)。因此有了这个词:依赖注入。我们通常依赖于Spring、Guice、Weld等DI框架来创建依赖关系并在需要时注入。
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;
}
}
其优点是:
分离对象的创建(换句话说,将使用与对象的创建分开)能够替换依赖项(例如:车轮、电池),而不改变使用它的类(汽车)促进“代码到接口而不是实现”原则在测试期间创建和使用模拟依赖关系的能力(如果我们想在测试期间使用模拟轮而不是真实实例,我们可以创建模拟轮对象并让DI框架注入Car)
其他回答
我将提出一个稍微不同的、简短而精确的依赖注入定义,侧重于主要目标,而不是技术手段(从这里开始):
依赖注入是创建静态、无状态的服务对象图,其中每个服务由其依赖关系。
我们在应用程序中创建的对象(无论我们是否使用Java、C#或其他面向对象语言)通常分为两类:无状态、静态和全局“服务对象”(模块),以及有状态、动态和本地“数据对象”。
模块图(服务对象图)通常在应用程序启动时创建。这可以使用容器(如Spring)完成,但也可以通过向对象构造函数传递参数来手动完成。这两种方法都有其优点和缺点,但在应用程序中使用DI肯定不需要框架。
一个要求是服务必须通过其依赖性进行参数化。这意味着什么完全取决于给定系统中采用的语言和方法。通常,这采用构造函数参数的形式,但使用setter也是一种选择。这也意味着(在调用服务方法时)对服务的用户隐藏服务的依赖关系。
何时使用?我会说,每当应用程序足够大时,将逻辑封装到单独的模块中,在模块之间使用依赖关系图,可以提高代码的可读性和可探索性。
使依赖注入概念易于理解。让我们以开关按钮为例来切换(打开/关闭)灯泡。
无依赖注入
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();
...
}
}`
例如,我们有两类客户机和服务。客户端将使用服务
public class Service {
public void doSomeThingInService() {
// ...
}
}
无依赖注入
方式1)
public class Client {
public void doSomeThingInClient() {
Service service = new Service();
service.doSomeThingInService();
}
}
方式2)
public class Client {
Service service = new Service();
public void doSomeThingInClient() {
service.doSomeThingInService();
}
}
方式3)
public class Client {
Service service;
public Client() {
service = new Service();
}
public void doSomeThingInClient() {
service.doSomeThingInService();
}
}
1) 2)3)使用
Client client = new Client();
client.doSomeThingInService();
优势
易于理解的
缺点
难以测试客户端类当我们更改Service构造函数时,我们需要在所有位置更改代码createService对象
使用依赖注入
方式1)构造函数注入
public class Client {
Service service;
Client(Service service) {
this.service = service;
}
// Example Client has 2 dependency
// Client(Service service, IDatabas database) {
// this.service = service;
// this.database = database;
// }
public void doSomeThingInClient() {
service.doSomeThingInService();
}
}
使用
Client client = new Client(new Service());
// Client client = new Client(new Service(), new SqliteDatabase());
client.doSomeThingInClient();
方式2)沉淀剂注入
public class Client {
Service service;
public void setService(Service service) {
this.service = service;
}
public void doSomeThingInClient() {
service.doSomeThingInService();
}
}
使用
Client client = new Client();
client.setService(new Service());
client.doSomeThingInClient();
方式3)接口注入
检查https://en.wikipedia.org/wiki/Dependency_injection
===
现在,这段代码已经遵循了依赖注入,测试客户端类更容易。然而,我们仍然多次使用新的Service(),并且在更改Service构造函数时效果不佳。为了防止这种情况,我们可以使用DI注射器1) 简单手动喷油器
public class Injector {
public static Service provideService(){
return new Service();
}
public static IDatabase provideDatatBase(){
return new SqliteDatabase();
}
public static ObjectA provideObjectA(){
return new ObjectA(provideService(...));
}
}
使用
Service service = Injector.provideService();
2) 使用库:适用于Android dagger2
优势
使测试更容易更改服务时,只需在Injector类中更改如果您使用使用构造函数注入,当您查看Client的构造函数时,您将看到Client类有多少依赖项
缺点
如果使用构造函数注入,则在创建客户端时创建服务对象,有时我们在客户端类中使用函数而不使用服务,因此创建的服务被浪费
依赖注入定义
https://en.wikipedia.org/wiki/Dependency_injection
依赖项是可以使用的对象(服务)注入是将依赖项(Service)传递给将使用它的依赖对象(Client)
依赖注入是与Spring框架相关概念的核心。在创建任何项目的框架时,Spring都可能发挥重要作用,而依赖注入就是其中之一。
实际上,假设在java中,您创建了两个不同的类,即类A和类B,并且无论类B中有什么函数,您都希望在类A中使用,所以此时可以使用依赖注入。在这里,您可以将一个类的对象放入另一个类中,就像您可以将整个类注入另一个类别中以使其可访问一样。通过这种方式可以克服依赖性。
依赖注入只是将两个类粘合在一起,同时保持它们的分离。
简单来说,依赖注入(DI)是消除不同对象之间的依赖关系或紧密耦合的方法。依赖注入为每个对象提供一个内聚行为。
DI是国际奥委会春季原则的实施,该原则说“不要打电话给我们,我们会打电话给你”。使用依赖注入程序员不需要使用new关键字创建对象。
对象一旦加载到Spring容器中,我们就可以在需要时重用它们,方法是使用getBean(StringbeanName)方法从Spring容器中获取这些对象。