.wj_nav { display: inline-block; width: 100%; margin-bottom: 0.375rem }
.wj_nav span { display: inline-block; margin-right: 0.375rem; color: rgba(102, 102, 102, 1) }
.wj_nav a, .wj_nav a:link, .wj_nav a:visited { color: rgba(51, 51, 51, 1); text-decoration: underline }
.wj_nav a:hover { color: rgba(255, 102, 0, 1); text-decoration: none }
.wj_title_1 { display: inline-block; width: 100%; margin-bottom: 1rem; border-left: 0.375rem solid rgba(255, 102, 0, 1); background-color: rgba(232, 232, 232, 1); font-size: 1.5rem; padding: 0.3125rem 0.625rem }
.wj_title_2 { display: inline-block; width: 100%; font-size: 1.25rem; font-weight: bold; margin-bottom: 1rem }
.wj_title_3 { display: inline-block; width: 100%; font-size: 1rem; font-weight: bold; margin-bottom: 1rem }
.wj_cont { line-height: 180%; margin-bottom: 1rem; font-size: 1rem }
.wj_img { display: inline-block; width: 100%; margin-bottom: 1rem }
.wj_code { margin-top: 0 !important; margin-bottom: 1rem !important; font-size: 0.875rem !important }
.wj_table { border: 1px solid rgba(255, 136, 56, 1); border-collapse: collapse; margin-bottom: 1rem }
.wj_table tr { display: table-row; vertical-align: inherit }
.wj_table tr th { display: table-cell; vertical-align: inherit; font-weight: normal; border: 1px solid rgba(255, 136, 56, 1); padding: 5px 10px; background-color: rgba(255, 102, 0, 1); color: rgba(255, 255, 255, 1); text-align: left }
.wj_table tr td { display: table-cell; vertical-align: inherit; border: 1px solid rgba(255, 136, 56, 1); padding: 0.3125rem 0.625rem; background-color: rgba(255, 255, 255, 1); text-align: left }
.wj_link, .wj_link:link, .wj_link:visited { color: rgba(51, 51, 51, 1); text-decoration: underline }
.wj_link:hover, .wj_link:active { color: rgba(255, 102, 0, 1) }
.wj_watermark, .wj_watermark :link, .wj_watermark :visited { color: rgba(255, 255, 255, 1) !important }
.wj_watermark :hover, .wj_link:active { color: rgba(255, 255, 255, 1) !important }
.wj_important { font-weight: bold; color: rgba(255, 102, 0, 1) }

导航

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)。

public class A
{
    private B _b;
    public A()
    {
        // 因为A掌控B的创建,因此A控制了B,此为“正转”
        _b = new B();
    }
}

2.2 依赖注入

对象之间的依赖不再由内部创建,而是由外部传递,称之为依赖注入(DI:Dependency Injection)。

控制反转是设计思想,依赖注入是实现手段。两者缺一不可:

public class A
{
    private B _b;     // B由外部注入,称之为依赖注入
    public A(B b)
    {
        // B由外部创建,脱离了A的控制,称之为控制反转
        _b = b;
    }
}

如上代码示例的是构造函数注入,另一种常见的依赖注入方式是属性注入:

public class A
{
    public B B { get; set; }
} void main()
{
    A a = new A();
    B b = new B();
    // 属性注入
    a.B = b;
}

3 为什么要使用依赖注入与控制反转

3.1 解耦

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

解耦除了完全消除依赖关系以外,另一种方式是将紧耦合转换为松耦合。先解释一下松紧耦合的概念,我们打开电脑机箱找到主机板上的南北桥芯片,可以看到它们是完全焊接在主板上的,这种不可替换的连接即为紧耦合;再找到内存条,发现它们可以拆卸并更换为其它品牌,这种可替换的连接即为松耦合。大部分时候,在软件设计开发时都应使用松散耦合,而依赖注入就是实现松散耦合非常好的一种方式。

如果我们再稍思考一下,主板上的内存条为什么能安装不同的品牌?原因是有相关技术标准,比如长宽尺寸,针脚数量,通信标准等,不同的内存条厂商,只要遵循标准生产出来的内存条就能安装到同一块主板上。在软件开发中,让主板支持不同厂商的内存条称之为可扩展性,定义内存条接口标准称之为抽象,根据标准生产内存条称之为面向抽象编程(或面向接口编程)。因此为了使软件模块具备更好的扩展性,除了使用依赖注入,还应注入抽象而非具体。

3.2 单元测试

不了解单元测试的小伙伴可先阅读我的另一篇文章《单元测试从入门到精通》。在单元测试中如果没有依赖注入,几乎寸步难行,通过简单的代码来示例:

难以测试的代码:

// 被测对象
public class House
{
    private Bedroom _bedroom;
    House() 
    { 
        // 内部构造协作对象,难以被测试。
        _bedroom = new Bedroom(); 
    }
    // ...
} // 测试用例
public void TestThisIsReallyHard()
{
    House house = new House();
    // 无法在测试过程中对Bedroom进行属性赋值、行为方法调用等,测试寸步难行
    // ...
}

易于测试的代码:

// 被测对象
public class House
{
    private Bedroom _bedroom;
    // 注入协作对象,可测试性好。
    House(Bedroom b) 
    { 
        _bedroom = b;
    }
    // ...
} // 测试用例
public void TestThisIsEasyAndFlexible()
{
    // Bedroom对象在掌控之中,易于测试
    Bedroom bedroom = new Bedroom();
    House house = new House(bedroom);
    // ...
}

4 Ioc容器

在稍复杂的软件产品中,通常会遇到两个关于对象的问题:一是对象的数量众多,如何统一对它们进行管理,比如统一管理对象的创建销毁过程,每个对象的生命周期;二是对象之间可能存在多重复杂的依赖关系,如何对这些依赖关系进行管理,比如谁先创建谁后创建,被依赖的对象如何注入依赖对象等。

针对如上两个问题的解决方案就是IoC容器(IoC Container),IoC容器是一个对象管理器,它统一管理对象的创建销毁过程、生命周期、依赖关系,以及提供自动注入、根据配置创建对象等一系列便捷功能。如下代码使用 Autofac(C#开源IoC容器)进行了简单示例:

// 使用开源IoC容器Autofac
using Autofac; namespace AutofacDemo
{
    class A
    { }     class B
    {
        A _a;
        // 只需要声明需要注入的对象,由容器自动完成依赖对象的创建与注入
        public B(A a)
        {
            _a = a;
        }
    }     internal class Program
    {
        static void Main(string[] args)
        {
            // 将类型注册至容器中
            ContainerBuilder builder = new ContainerBuilder();
            builder.RegisterType<A>();
            // 设置对象的生命周期(单例模式)
            builder.RegisterType<B>().SingleInstance();             // 构造IoC容器
            IContainer container = builder.Build();
            // 从容器中获取对象
            B b = container.Resolve<B>();
        }
    }
}

5 结束语

依赖注入与控制反转的思想诞生于软件开发追求高内聚、低耦合的历史进程中,20世纪90年代末已在软件设计模式、单元测试中使用。2002年Java的Spring框架搭载IoC容器、AOP等大杀器风靡全球,DI与IoC被更多的开发者关注。直到最近的项目中涉及插件化框架,而IoC容器又是插件架构的最佳拍档,因此将其整理成文。如能给人予帮助,不甚荣幸。

<全文完>

依赖注入(DI)与控制反转(IoC)的更多相关文章

  1. 依赖注入(DI)和控制反转(IOC)

    依赖注入(DI)和控制反转(IOC) 0X1 什么是依赖注入 依赖注入(Dependency Injection),是这样一个过程:某客户类只依赖于服务类的一个接口,而不依赖于具体服务类,所以客户类只 ...

  2. PHP依赖注入(DI)和控制反转(IoC)详解

    这篇文章主要介绍了PHP依赖注入(DI)和控制反转(IoC)的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 首先依赖注入和控制反转说的是同一个东西,是一种设计模式,这种设计模式用来减少程 ...

  3. 依赖注入(DI)和控制反转(IOC)的理解,写的太好了。

    学习过spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的,今天和大家 ...

  4. 【串线篇】依赖注入DI与控制反转IOC

    DI&IOC 在spring框架中DI与IOC说的其实是一回事 一句话:本来我接受各种参数来构造一个对象,现在只接受一个参数——已经实例化的对象. 也就是说我对对象的『依赖』是注入进来的,而和 ...

  5. 轻松理解 Java开发中的依赖注入(DI)和控制反转(IOC)

    前言 关于这个话题, 网上有很多文章,这里, 我希望通过最简单的话语与大家分享. 依赖注入和控制反转两个概念让很多初学这迷惑, 觉得玄之又玄,高深莫测. 这里想先说明两点: 依赖注入和控制反转不是高级 ...

  6. 话说 依赖注入(DI) or 控制反转(IoC)

    科普:首先依赖注入和控制反转说的是同一个东西,是一种设计模式,这种设计模式用来减少程序间的耦合,鄙人学习了一下,看TP官网还没有相关的文章,就写下这篇拙作介绍一下这种设计模式,希望能为TP社区贡献一些 ...

  7. ASP.NET MVC进阶之路:深入理解依赖注入(DI)和控制反转(IOC)

    0X1 什么是依赖注入 依赖注入(Dependency Injection),是这样一个过程:某客户类只依赖于服务类的一个接口,而不依赖于具体服务类,所以客户类只定义一个注入点.在程序运行过程中,客户 ...

  8. PHP 依赖注入(DI) 和 控制反转(IoC)

    要想理解 PHP 依赖注入 和 控制反转 两个概念,就必须搞清楚如下的两个问题: DI —— Dependency Injection 依赖注入 IoC —— Inversion of Control ...

  9. 聊一聊PHP的依赖注入(DI) 和 控制反转(IoC)

    简介 IoC Inversion of Control 控制反转DI Dependency Injection 依赖注入 依赖注入和控制反转说的实际上是同一种东西,它们是一种设计模式,这种设计模式用来 ...

  10. 依赖注入(DI)与控制反转(IOC)

    DI(依赖注入,Dependency Injection),和所谓的IoC(控制反转,Inversion of Control )是一个意思. DI是一种通过接口实现松耦合的设计模式.初学者可能会好奇 ...

随机推荐

  1. Qt编写物联网管理平台47-通用数据库设置

    一.前言 为了做这个通用的数据库组件,专门安装了虚拟机来安装各种版本的不同类型的数据库做测试,包括编译对应的数据库插件,我一直坚信的是一切从实际出发+有实际采用发言权,包括不同Qt版本编译mysql. ...

  2. Qt Creator 5.0 发布

    我们很高兴地宣布 Qt Creator 5.0 的发布! 正如4.15 发布博文中所宣布的,我们将切换到语义版本控制方案,因此这是 Qt Creator 很长一段时间以来的第一次主要版本更新!不过不要 ...

  3. [转]升级/重装win10系统--提示无法验证密钥的解决办法

    在由win7系统升级到win10时,出现了无法验证密钥的问题(还未提示输入密钥的时候就直接提示无法验证密钥),英文版报错为:Windows 10 setup has failed to validat ...

  4. ABP 系列总结

    2019年第一次接触 ABP 框架,那时候还是比较笨重的旧版本的,后来升级到 vNext 版本,我也基于 ABP 模块化的设计方式开发了一些模块用于日常工作.这个系列主要为了系统地记录一下日常工作与学 ...

  5. java第二章数组学习

    java第二章数组 数组的概念和特点 数组(Array),是多个相同类型数据按一定顺序排列的集合,并使用一个 名字命名,并通过编号的方式对这些数据进行统一管理. 特点 数组本身是引用数据类型,而数组中 ...

  6. shell脚本中的循环(for循环,while循环)及break、continue的用法示例

    Shell脚本中也算是一门简易的编程语言了,当然循环是不能缺少的.常用到的循环有for循环和while循环.下面就分别介绍一下两种循环的结构. [for循环]: Shell脚本中的for循环示例: # ...

  7. 使用GraalVM将SpringBoot工程编译成平台原生的可执行文件

    原文链接:https://blog.liuzijian.com/post/209e68d0-a418-1737-503a-d47e6d2d9350.html 1.GraalVM GraalVM (ht ...

  8. 数组中的常见异常: 1. 数组角标越界的异常:ArrayIndexOutOfBoundsExcetion 2. 空指针异常:NullPointerException

    数组中的常见异常:  1. 数组角标越界的异常:ArrayIndexOutOfBoundsExcetion   2. 空指针异常:NullPointerException package com.ch ...

  9. dart中Set类型详解

    01==> Set 它的主要功能是去除重复的数组内容: Set是没有顺序且不能够重复的数组,所以不能够通过索引值去获取内容 var s = new Set(); s.add('苹果'); s.a ...

  10. MacOS15+Xcode版本16+对ReactNative项目进行编译和上传到APPStore的踩坑记录

    作者:Kovli 重要通知:红宝书第5版2024年12月1日出炉了,感兴趣的可以去看看,https://u.jd.com/saQw1vP 红宝书第五版中文版 红宝书第五版英文原版pdf下载(访问密码: ...