依赖注入——人生若只如初见

前端之家收集整理的这篇文章主要介绍了依赖注入——人生若只如初见前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

刚接触一门学问或技术,就像刚认识一位可以当做潜在对象的异性,幸福又美好。

当接触的久了,了解的多了,习以为常了,就没有了当初的兴奋和快乐。

正所谓,人生若只如初见。

初识依赖注入,甚是美好,解决我在开发过程中的诸多疑惑,相见恨晚。但愿人长久。

啥是依赖注入

俗话说的好,“Talk is cheap. Show me the code.”翻译过来就是:空谈误国,代码兴邦。咱们不空谈理论,先看一个例子。

假设一种情景,类/接口(以下统称类) Client 要在其 greet() 方法中使用类 Service,所以 Client 实例里面会有一个 Service 实例的引用,而且要将该引用指向一个 Service 实例(这不废话吗?!)。

如何在 Client 实例中得到 Service 实例?实现方法很灵活(额,我讨厌灵活,越是灵活我越是不知所措),我们列举一下,不外乎有 4 种方式:

  • Client 中构造 Service 实例
  • 在 Client 构造方法参数中传入 Service 实例
  • 从 setter 方法中传入 Service 实例
  • 通过接口方法传入 Service 实例

下面一一实现。

在 Client 中构造

  1. public class Client {
  2.  
  3. Service mService;
  4.  
  5. public Client() {
  6. mService = new ConcreteService();
  7. }
  8.  
  9. public String greet() {
  10. return "Hello " + mService.getName();
  11. }
  12. }

so easy,信手拈来。就是在 Client 的构造函数中 new 一个ConcreteService 实例而已。

在 Client 构造方法中传入

  1. public class Client {
  2.  
  3. Service mService;
  4.  
  5. public Client(Service service) {
  6. mService = service;
  7. }
  8.  
  9. public String greet() {
  10. return "Hello " + mService.getName();
  11. }
  12. }

也不难,就是将在 Client 外面构造好的 Service 实例通过 Client 的构造方法的参数传进去而已。

setter 方法传入

  1. public class Client {
  2.  
  3. Service mService;
  4.  
  5. public void setService(Service service) {
  6. mService = service;
  7. }
  8.  
  9. // Set the other service to be used by this client
  10. public void setOtherService(Service otherService) {
  11. if (otherService == null) {
  12. throw new InvalidParameterException("otherService must not be null");
  13. }
  14. this.otherService = otherService;
  15. }
  16.  
  17. public String greet() {
  18. return "Hello " + mService.getName();
  19. }
  20. }

在 Client 中写一个 setter 方法,通过该方法将 Service 实例传进去。

通过接口的方法传入

  1. public interface ServiceSetter {
  2. void setService(Service servcie);
  3. }
  4.  
  5. public class Client implements ServiceSetter {
  6.  
  7. Service mService;
  8.  
  9. @Override
  10. public void setService(Service service) {
  11. mService = service;
  12. }
  13.  
  14. public String greet() {
  15. return "Hello " + mService.getName();
  16. }
  17. }

Client 实现 ServiceSetter 接口,实现 setService() 方法,通过该方法将 Service 实例传给 Client 实例。

好了,4 中实现方法枚举完了。请各位看官半个小板凳,我们要开讲重头戏了。

在这个例子中,Service 是被使用者,是依赖(Dependency);Client 是使用者。

将 Service 实例从 Client 的构造方法或 setter 方法中传进去的过程叫做注入(Injection)。注意此处的注入是名词,而非动词。

而负责传递过程的构造方法或 setter 方法称为注入者(Injector)。

简单来说,就是被使用者在使用者外面构造好,然后传递给使用者。所以,除了第1种实现方法,其余3种都是依赖注入。第1种方法直接在使用者内部构造依赖,不属于依赖注入。

常见类型比较

如上,依赖注入共有3种类型:

  • 在 Client 构造方法参数中传入 Service 实例
  • 从 setter 方法中传入 Service 实例
  • 通过接口方法传入 Service 实例

那么,它们各自有什么优劣呢?

在 Client 构造方法中传入

优势

依赖一次性的被传进来,保证了 Client 使用依赖的时候所有依赖都是可用的,不用关心其是否为空。

劣势

不够灵活,必须要在 Client 被构造之前构造好依赖,而实际情况中依赖可能后于 Client 被构造好,而且可能会根据具体情况(例如主客态)在不同的时候对同一个 Client 设置不同的依赖。

从 setter 方法中传入 Service 实例

优势

足够灵活,可以在适当的时候设置合适的依赖。

劣势

由于调用 setter 方法的时机未知,所以在使用依赖的时候,必须花费额外的精力去保证所有依赖都是可用的,依赖不能在调用 setter 方法之前被使用。

  1. // Set the service to be used by this client
  2. public void setService(Service service) {
  3. this.service = service;
  4. }
  5.  
  6. // Set the other service to be used by this client
  7. public void setOtherService(Service otherService) {
  8. this.otherService = otherService;
  9. }
  10.  
  11. // Check the service references of this client
  12. private void validateState() {
  13. if (service == null) {
  14. throw new IllegalStateException("service must not be null");
  15. }
  16. if (otherService == null) {
  17. throw new IllegalStateException("otherService must not be null");
  18. }
  19. }
  20.  
  21. // Method that uses the service references
  22. public void doSomething() {
  23. validateState();
  24. service.doYourThing();
  25. otherService.doYourThing();
  26. }

而且更为糟糕的是,当多个依赖之间有相关性的时候,我们必须保证多个依赖的 setter 方法调用顺序不会影响实际效果,这将大大增加代码维护的难度。

通过接口方法传入 Service 实例

优势

方法可以不用关心 Client 的实现细节,只需遵守接口的方法规则,将依赖(名词)本身的引用通过 setter 设置到 Client 中去,即可达到同样的效果

  1. public abstract class Service {
  2. public abstract String getName();
  3. }
  4.  
  5. public interface ServiceSetter {
  6. void setService(Service servcie);
  7. }
  8.  
  9. public class Client implements ServiceSetter {
  10.  
  11. Service mService;
  12.  
  13. @Override
  14. public void setService(Service service) {
  15. mService = service;
  16. }
  17.  
  18. public String greet() {
  19. return "Hello " + mService.getName();
  20. }
  21. }
  22.  
  23. public class ConcreteService extends Service {
  24.  
  25. @Override
  26. public String getName() {
  27. // TODO Auto-generated method stub
  28. return null;
  29. }
  30.  
  31. public void setServiceSetter(ServiceSetter s) {
  32. s.setService(this);
  33. }
  34. }
  35.  
  36. public class Injector {
  37. public static void main(String... args) {
  38. ConcreteService service = new ConcreteService();
  39.  
  40. Client c = new Client();
  41.  
  42. service.setServiceSetter(c);
  43. }
  44. }

劣势

控制反转

依赖倒置

Dagger

更多资料

猜你在找的设计模式相关文章