找回密码
 立即注册
首页 业界区 业界 依赖注入(DI)与控制反转(IoC)

依赖注入(DI)与控制反转(IoC)

寅斫 5 天前
导航
1前言
2什么是依赖注入与控制反转
2.1控制反转
2.2依赖注入
3为什么要使用依赖注入与控制反转
3.1解耦
3.2单元测试
4IoC容器
5结束语
1 前言
依赖注入(DI)与控制反转(IoC)可能是一些开发小伙伴耳熟但又不能详的两个词,经常看到它们的名字,但又不理解。这两个词来源于英文直译,看似高深莫测,其实非常简单,并且在一些开发场景中扮演着不可或缺的角色,比如单元测试离不开依赖注入,IoC容器是插件框架的最佳拍档等,本文尝试以最简单的方式阐述这两种思想在开发中的应用。文章来源:https://www.wubayue.com
2 什么是依赖注入与控制反转
2.1 控制反转
在解释控制反转前,首先需要理解什么是“正转”:A依赖于B,并且A掌控B的创建销毁,此时A控制了B,即为“正转”。
当B的创建销毁在A之外完成,B脱离了A的控制,称之为控制反转(IoC:Invertion of Control)。
  1. public class A
  2. {
  3.     private B _b;
  4.     public A()
  5.     {
  6.         // 因为A掌控B的创建,因此A控制了B,此为“正转”
  7.         _b = new B();
  8.     }
  9. }
复制代码
2.2 依赖注入
对象之间的依赖不再由内部创建,而是由外部传递,称之为依赖注入(DI:Dependency Injection)。
控制反转是设计思想,依赖注入是实现手段。两者缺一不可:
  1. public class A
  2. {
  3.     private B _b;
  4.     // B由外部注入,称之为依赖注入
  5.     public A(B b)
  6.     {
  7.         // B由外部创建,脱离了A的控制,称之为控制反转
  8.         _b = b;
  9.     }
  10. }
复制代码
如上代码示例的是构造函数注入,另一种常见的依赖注入方式是属性注入:
  1. public class A
  2. {
  3.     public B B { get; set; }
  4. }
  5. void main()
  6. {
  7.     A a = new A();
  8.     B b = new B();
  9.     // 属性注入
  10.     a.B = b;
  11. }
复制代码
3 为什么要使用依赖注入与控制反转
3.1 解耦
1.jpeg

在软件行业,有一条黄金法则叫“高内聚,低耦合”。耦合表示使用(或称为依赖),比如B使用了A,即B耦合了A,只要类的数量一多,类之间千丝万缕的耦合关系会成为巨大挑战,高内聚就是把相同的功能放在一起,这样类之间的耦合关系就会减少,通过提升内聚来减少类之间的耦合是一种常见的解耦方式。如上图,C依赖B,B依赖A,原本是两级依赖关系,通过将B中的部分功能向A内聚(前提是这部分功能原本就具有相关性),实现了B、C都依赖于A的一级依赖关系,B、C之间完成了解耦。
2.jpeg

解耦除了完全消除依赖关系以外,另一种方式是将紧耦合转换为松耦合。先解释一下松紧耦合的概念,我们打开电脑机箱找到主机板上的南北桥芯片,可以看到它们是完全焊接在主板上的,这种不可替换的连接即为紧耦合;再找到内存条,发现它们可以拆卸并更换为其它品牌,这种可替换的连接即为松耦合。大部分时候,在软件设计开发时都应使用松散耦合,而依赖注入就是实现松散耦合非常好的一种方式。
如果我们再稍思考一下,主板上的内存条为什么能安装不同的品牌?原因是有相关技术标准,比如长宽尺寸,针脚数量,通信标准等,不同的内存条厂商,只要遵循标准生产出来的内存条就能安装到同一块主板上。在软件开发中,让主板支持不同厂商的内存条称之为可扩展性,定义内存条接口标准称之为抽象,根据标准生产内存条称之为面向抽象编程(或面向接口编程)。因此为了使软件模块具备更好的扩展性,除了使用依赖注入,还应注入抽象而非具体。
3.2 单元测试
不了解单元测试的小伙伴可先阅读我的另一篇文章《单元测试从入门到精通》。在单元测试中如果没有依赖注入,几乎寸步难行,通过简单的代码来示例:
难以测试的代码:
  1. // 被测对象
  2. public class House
  3. {
  4.     private Bedroom _bedroom;
  5.     House() 
  6.     { 
  7.         // 内部构造协作对象,难以被测试。
  8.         _bedroom = new Bedroom(); 
  9.     }
  10.     // ...
  11. }
  12. // 测试用例
  13. public void TestThisIsReallyHard()
  14. {
  15.     House house = new House();
  16.     // 无法在测试过程中对Bedroom进行属性赋值、行为方法调用等,测试寸步难行
  17.     // ...
  18. }
复制代码
易于测试的代码:
  1. // 被测对象
  2. public class House
  3. {
  4.     private Bedroom _bedroom;
  5.     // 注入协作对象,可测试性好。
  6.     House(Bedroom b) 
  7.     { 
  8.         _bedroom = b;
  9.     }
  10.     // ...
  11. }
  12. // 测试用例
  13. public void TestThisIsEasyAndFlexible()
  14. {
  15.     // Bedroom对象在掌控之中,易于测试
  16.     Bedroom bedroom = new Bedroom();
  17.     House house = new House(bedroom);
  18.     // ...
  19. }
复制代码
4 Ioc容器
3.jpeg

在稍复杂的软件产品中,通常会遇到两个关于对象的问题:一是对象的数量众多,如何统一对它们进行管理,比如统一管理对象的创建销毁过程,每个对象的生命周期;二是对象之间可能存在多重复杂的依赖关系,如何对这些依赖关系进行管理,比如谁先创建谁后创建,被依赖的对象如何注入依赖对象等。
针对如上两个问题的解决方案就是IoC容器(IoC Container),IoC容器是一个对象管理器,它统一管理对象的创建销毁过程、生命周期、依赖关系,以及提供自动注入、根据配置创建对象等一系列便捷功能。如下代码使用 Autofac(C#开源IoC容器)进行了简单示例:
  1. // 使用开源IoC容器Autofac
  2. using Autofac;
  3. namespace AutofacDemo
  4. {
  5.     class A
  6.     { }
  7.     class B
  8.     {
  9.         A _a;
  10.         // 只需要声明需要注入的对象,由容器自动完成依赖对象的创建与注入
  11.         public B(A a)
  12.         {
  13.             _a = a;
  14.         }
  15.     }
  16.     internal class Program
  17.     {
  18.         static void Main(string[] args)
  19.         {
  20.             // 将类型注册至容器中
  21.             ContainerBuilder builder = new ContainerBuilder();
  22.             builder.RegisterType();
  23.             // 设置对象的生命周期(单例模式)
  24.             builder.RegisterType<B>().SingleInstance();
  25.             // 构造IoC容器
  26.             IContainer container = builder.Build();
  27.             // 从容器中获取对象
  28.             B b = container.Resolve<B>();
  29.         }
  30.     }
  31. }
复制代码
5 结束语
依赖注入与控制反转的思想诞生于软件开发追求高内聚、低耦合的历史进程中,20世纪90年代末已在软件设计模式、单元测试中使用。2002年Java的Spring框架搭载IoC容器、AOP等大杀器风靡全球,DI与IoC被更多的开发者关注。直到最近的项目中涉及插件化框架,而IoC容器又是插件架构的最佳拍档,因此将其整理成文。如能给人予帮助,不甚荣幸。


来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册