引子
在尝试使用 Scalable Frontend 1 — Architecture Fundamentals 外面所说的 Dependency Injection(依赖注入)时,感觉有些不清不楚,找了些材料,才更明确了一些,在此翻译记录局部聚合。
- Origin
- My GitHub
依赖注入是什么
在软件工程中,依赖注入是一种一个对象接管它所依赖的其它对象的技术。这些其它对象称为依赖。在典型的“应用”关系中,接管对象称为客户端(client),传递的(即“注入的”)对象称为服务(service)。将服务传递给客户端的代码能够是多种类型的货色,称为注入器。注入器通知客户端要应用什么服务,而不是客户端指定要应用哪个服务。“注入”是指将依赖项(服务)传递到将应用它的对象(客户端)中。
服务是客户端状态的一部分。将服务传递给客户端,而不是让客户端构建或查找服务,是该模式的根本要求。
劣势
- 依赖注入容许客户端灵便地进行配置。只有客户端的行为是固定的。客户端能够对任何反对客户端冀望的外在接口的货色进行操作。
- 依赖注入可用于将零碎的配置详细信息内部化到配置文件中,从而容许在不从新编译的状况下重新配置零碎。能够针对组件须要不同实现的不同状况编写独自的配置。这包含但不限于测试。
- 因为依赖注入不须要对代码行为进行任何更改,所以它能够利用于遗留代码的重构。后果就是客户端更加独立,并且更容易应用存根或模仿对象进行单元测试。这种易测试性通常是应用依赖注入时留神到的第一个益处。
- 依赖注入容许客户端移除具体实现所须要应用的的所有常识。这有助于将客户端与设计更改和缺点的影响隔离开来。它加强了可重用性、可测试性和可维护性。
- 缩小应用程序对象中的样板代码,因为初始化或设置依赖项的所有工作都由提供程序组件解决。
- 依赖注入容许并行或独立开发。两个开发人员能够独立开发互相应用的类,而只须要晓得类将通过什么接口进行通信。插件通常是由第三方商店开发的,他们甚至从不与开发应用插件的产品的开发人员交换。
- 依赖项注入缩小了类与其依赖项之间的耦合。
劣势
- 依赖注入创立的客户端须要由结构代码提供配置细节。当显著的默认值可用时,这可能会沉重。
- 依赖注入会使代码难以跟踪(读取),因为它将行为与结构拆散开来。这意味着开发人员必须援用更多的文件来跟踪零碎的执行状况。
- 依赖注入框架是通过反射或动静编程实现的。这会障碍 IDE 自动化的应用,例如“查找援用”、“显示调用层级”和平安重构。
- 依赖注入通常须要更多的后期开发工作,因为不能在须要的工夫和地点招集某样货色成为正确的货色,但必须要求被注入,并且确保它已被注入。
- 依赖注入迫使复杂性从类转移到类之间的分割中,可能并不总是现实的或容易治理的。
- 依赖注入可能助长基于依赖注入框架的依赖。
依赖注入的几种模式
客户端对象至多有三种形式能够接管对外部模块的援用:结构器注入、设置器注入、接口注入。
结构器注入
依赖项是通过客户端的类构器提供的参数传入。
// Constructor
Client(Service service) {
// Save the reference to the passed-in service inside this client
this.service = service;
}
当能够首先结构所有依赖项时,最好应用它,因为它能够用来确保客户端对象始终处于无效状态,而不是使其某些依赖项援用为 null(不设置)。但就其自身而言,它不足在当前更改其依赖项的灵活性。这可能是使客户端不可变从而实现线程平安的第一步。
// Constructor
Client(Service service, Service otherService) {if (service == null) {throw new InvalidParameterException("service must not be null");
}
if (otherService == null) {throw new InvalidParameterException("otherService must not be null");
}
// Save the service references inside this client
this.service = service;
this.otherService = otherService;
}
设置器注入
这种形式须要客户端提供一个设置器给依赖性。
// Setter method
public void setService(Service service) {
// Save the reference to the passed-in service inside this client.
this.service = service;
}
要求客户端为每个依赖项提供设置器。这样就能够随时自在地操作依赖项援用的状态。这提供了灵活性,然而如果要注入多个依赖项,那么客户端很难确保在可能应用之前已注入所有依赖项。
因为这些注入是独立产生的,所以无奈判断注入器何时实现了与客户机的连贯。依赖项可能仅仅因为调用其设置器失败而保留为 null。这将强制查看从客户端组装时到应用时的注入是否已实现。
// Set the service to be used by this client
public void setService(Service service) {this.service = service;}
// Set the other service to be used by this client
public void setOtherService(Service otherService) {this.otherService = otherService;}
// Check the service references of this client
private void validateState() {if (service == null) {throw new IllegalStateException("service must not be null");
}
if (otherService == null) {throw new IllegalStateException("otherService must not be null");
}
}
// Method that uses the service references
public void doSomething() {validateState();
service.doYourThing();
otherService.doYourThing();}
接口注入
这只是客户端将角色接口公布到客户端依赖项的设置办法。它能够用来确定在注入依赖项时注入器应该如何与客户端对话。
// Service setter interface.
public interface ServiceSetter {public void setService(Service service);
}
// Client class
public class Client implements ServiceSetter {
// Internal reference to the service used by this client.
private Service service;
// Set the service that this client is to use.
@Override
public void setService(Service service) {this.service = service;}
}
接口注入的长处是依赖项能够齐全疏忽它们的客户端,然而依然能够接管对新客户端的援用,并应用它,将本身的援用发送回客户端。这样,依赖项就变成了注入器。要害是注入办法(可能只是一个经典的设置器办法)是通过一个接口提供的。
参考资料
- Dependency injection