zoukankan      html  css  js  c++  java
  • 依赖注入

    目录

    引子

    在尝试运用 Scalable Frontend 1 — Architecture Fundamentals 里面所说的 Dependency Injection(依赖注入)时,感觉有些不清不楚,找了些资料,才更明白了一些,在此翻译记录部分聚合。

    依赖注入是什么

    在软件工程中,依赖注入是一种一个对象接收它所依赖的其它对象的技术。这些其它对象称为依赖。在典型的“使用”关系中,接收对象称为客户端(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;
        }
    }
    

    接口注入的优点是依赖项可以完全忽略它们的客户端,但是仍然可以接收对新客户端的引用,并使用它,将自身的引用发送回客户端。这样,依赖项就变成了注入器。关键是注入方法(可能只是一个经典的设置器方法)是通过一个接口提供的。

    参考资料

  • 相关阅读:
    【BZOJ】1620: [Usaco2008 Nov]Time Management 时间管理(贪心)
    【BZOJ】1651: [Usaco2006 Feb]Stall Reservations 专用牛棚(线段树/前缀和 + 差分)
    【BZOJ】1628 && 1683: [Usaco2007 Demo]City skyline 城市地平线(单调栈)
    【BZOJ】1624: [Usaco2008 Open] Clear And Present Danger 寻宝之路(floyd)
    【BZOJ】1622: [Usaco2008 Open]Word Power 名字的能量(dp/-模拟)
    【BZOJ】1634: [Usaco2007 Jan]Protecting the Flowers 护花(贪心)
    【BZOJ】1690: [Usaco2007 Dec]奶牛的旅行(分数规划+spfa)
    【BZOJ】1660: [Usaco2006 Nov]Bad Hair Day 乱发节(单调栈)
    【BZOJ】1642: [Usaco2007 Nov]Milking Time 挤奶时间(dp)
    【BZOJ】1629: [Usaco2007 Demo]Cow Acrobats(贪心+排序)
  • 原文地址:https://www.cnblogs.com/thyshare/p/14857882.html
Copyright © 2011-2022 走看看