热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

深入浅出的理解依赖注入(DI)和控制反转(IoC)的原理

前言依赖注入(DI)和控制反转(IoC)在现代研发技术上已经不陌生了,而陌生的却是应用这门技术的很多工程师,网上的很多资料大多数都是讲解如何使用框架来

前言

依赖注入(DI)和控制反转(IoC)在现代研发技术上已经不陌生了,而陌生的却是应用这门技术的很多工程师,网上的很多资料大多数都是讲解如何使用框架来实现,偏于执行层面,而我这篇文章则偏于概念,让你彻底理解他们两者的关系以及原理和场景。

但很多人则把这两个概念搞混淆了,通俗地说:控制反转(IoC)是一种设计理念,依赖注入(DI)是这种理念的实践。

所有代码都将使用 “用户管理” 的作为示例,目的是用最通用的例子说清楚最根本的问题。


控制反转(IoC)

何为控制反转?这是很多文章一开始的标题,我也不例外,让不懂的小伙伴也不需要到处找文章了。先看一段代码。

public class UserService
{public int CreateUser(User user){//to do}
}

这段代码很简单,我要创建一个用户,但是我没写方法的内容。思考一下,我现在写一个业务逻辑的方法用于 CRUD,那我是不是得先知道我用什么来操作数据库?

我的可选方案有:


  • 原生的 ADO.NET
  • 找一个现成的 SqlHelper
  • 使用微软的 EntityFramework
  • 其他第三方框架 Dapper/PetaPoco/…

如果我选择 EntityFramework 的话,那我的代码就会变成这个样子:

public int CreateUser(User user)
{using(var context = new UserDbContext()){context.Users.Add(user);return context.SaveChanges();}
}

这样的代码没有问题,但说不定哪一天,领导来一句:“需求改了,我们要用 Dapper 来做数据库操作”,我想你肯定已经去外面找家伙,恨不得一锥子砸死这xxxx的领导。

一个系统不可能只有这么一个方法逻辑,肯定大量充斥并显示地 new 对象。如果领导这么坚持,懵逼的你要么走人(让新来的给你擦屁股,一种不负责的表现),要么改(把自己逼疯的结果),你怎么办?

因此就出现了一种设计模式,叫 控制反转=策略模式


就是把实例化的结果暴露给调用方,而不是实现方。


怎么定义调用方和实现方?


实现这个方法的就是实现方。调用你这个方法的就是调用方。


就好比,你会关心 EF 里面的 Add 方法是怎么实现的吗?你只是在需要的时候调用这个 Add 方法就好了。

所以为了满足领导的需求,我们需要封装一下:

声明一个接口,定义实现的规范:

public interface IDbContext
{int Insert<T>(T item);//..
}

然后再使用到我们的业务逻辑类中&#xff1a;

public class UserService
{private readonly IDbContext _context;public UserService(IDbContext context){_context &#61; context;}public int CreateUser(User user){return _context.Insert(user);}
}

构造方法的参数用于暴露给调用方&#xff0c;寓意如下&#xff1a;


让调用方在使用我这个方法时&#xff0c;必须要给我一个实现了 IDbContext 接口的实例&#xff08;因为有一个构造方法参数&#xff09;&#xff0c;怎么实现的实现方就不管了。


就是把实例化的控制权移交给了调用方&#xff0c;这就是控制反转。

调用方在使用时就得这样&#xff1a;

var userService &#61; new UserService(new EFDbContext()); //提供 IDbContext 的实现
userService.CreateUser(new User()); // 调用
//..

但聪明的朋友会慢慢发现&#xff0c;其实我这个调用方到时候也充斥着大量地 new 操作&#xff0c;虽然也可以通过刚才的控制反转把要实例的内容暴露给调用方&#xff0c;但问题来了&#xff0c;什么时候是个头&#xff1f;

也有很多聪明的朋友会回答&#xff1a;在表现层的时候给实例。这个答案倒没什么错&#xff0c;但如果我一个类里&#xff0c;定义了很多个构造方法的参数呢&#xff1f;

public class MyService
{public MyService(IDbContext context, IUserRepository userRepository, IEmailService emailService, IInfoManager manager ...)
}

难不成你都自己 new 一次&#xff1f;

new MyService(new EFDbContext(), new UserRepository(new EFDbContext()), new EmailService(new UserRepository(new EFDbContext())), //....)

我只有一个感想&#xff0c;只有 SB 才这样写。所以&#xff0c;就出现了依赖注入。


依赖注入

什么叫依赖&#xff1f;什么叫注入&#xff1f;


人依赖氧气才能活着&#xff1b;家庭里依赖父母、亲人、爱人&#xff1b;学习依赖老师&#xff1b;晋升依赖于领导的提拔&#xff1b;租房子依赖于收入等等。


回到最初的一段代码上&#xff1a;

public class UserService
{private readonly IDbContext _context;public UserService(IDbContext context){_context &#61; context;}public int CreateUser(User user){return _context.Insert(user);}
}

我要操作数据库&#xff0c;依赖的是 IDbContext 这接口的实现对象。但是怎么实现的&#xff0c;我就不用管了

注入这个词怎么理解呢&#xff1f;
在汉语词典中的解释&#xff1a;


  • 1.泵入、灌入或流入。
  • 2.以气息传送。
  • 3.使产生对某物的印象或得到逐渐灌输。

一般注入指的是液体&#xff0c;一层一层的流下去&#xff1b;而我们的架构也是一层一层的进行封装&#xff08;表现层->逻辑层->数据层&#xff09;&#xff1b;
注入的形象


如果团队里有不同的人&#xff0c;张三喜欢和啤酒&#xff0c;李四喜欢和白酒&#xff0c;于是乎&#xff0c;张三从第二层开始倒啤酒&#xff0c;李四从第四层开始倒白酒&#xff0c;试想一下&#xff0c;最底下的酒杯里的酒会是什么味道&#xff1f;


因此&#xff0c;我们规定好了&#xff0c;只能从最顶上倒酒&#xff0c;这样下面留下来的酒都是同一种味道&#xff08;如下图&#xff09;
统一容器里的内容

而换成技术来说&#xff0c;这种容器就有 Autofac、Unit、Sprint 等等。


实现原理

通过这个图我们就知道了&#xff0c;在注入的一开始&#xff0c;我们需要有一个容器&#xff0c;容器里装好了我们的内容&#xff0c;有可能这个容器里的酒是混合型的。

所以&#xff0c;在程序上&#xff0c;所有的注入配置都必须写在程序启动的时候&#xff0c;例如 Mvc 里的 Global 里&#xff0c;AspNetCore 的 ConfigureServices 里。

这里面需要提到一个技术概念&#xff1a;服务和实例

我们把依赖的对象叫做服务&#xff0c;把这个服务所对应的对象叫做实例。


你去消费&#xff0c;服务员给你提供的一系列服务&#xff0c;但你并不知道这些服务背后他们经历的培训有多少&#xff0c;但你实现了你的目标&#xff0c;那就是享受服务&#xff0c;满足需求。


我们回到代码层面&#xff1a;

public class UserService
{private readonly IDbContext _context;public UserService(IDbContext context) //这就是提供的服务{_context &#61; context;}
}

而我们在最外层使用了 Autofac 技术

var builder &#61; new ContainerBuilder(); //准备好容器
builder.RegisterType<EFDbContext>().As<IDbContext>(); //服务对应的实例

你可以理解成&#xff1a;当遇到 服务 时&#xff0c;容器查找会已经注册过的 实例


就好比你去西餐厅点一道麻婆豆腐&#xff0c;餐厅不提供这道菜&#xff0c;你就满足不了需求。



生命周期

当然依赖注入框架还解决了一个问题&#xff0c;就是对于服务声明周期的管理。声明周期这个就需要和 GC 的概念挂钩了&#xff0c;如果你人为的 new 各种实例&#xff0c;我想没几个人会去主动释放掉没有用处的实例吧&#xff1f;

因此&#xff0c;依赖注入框架就把声明周期管理加入其中&#xff0c;帮助提高了性能。基本的就是三种类型&#xff1a;瞬间的、范围的、单例的。


  • 瞬间的
    每一次的访问&#xff0c;实例都是不一样的。

张三每一次访问这个页面&#xff0c;拿到的结果都不同。



  • 范围的
    在同一次访问时&#xff0c;实例都是一样的。

只要是张三访问&#xff0c;不管访问多少次&#xff0c;这些实例都是一样的。



  • 单例的
    任何时候&#xff0c;实例都是一样的。

无论张三还是李四&#xff0c;实例都是一样的。



总结

现在是不是已经深入了解依赖注入和控制反转他们俩的关系&#xff0c;以及为什么的问题了&#xff1f;

我们可以通过下图进行一次总结&#xff1a;
依赖注入标准设计


推荐阅读
  • web.py开发web 第八章 Formalchemy 服务端验证方法
    本文介绍了在web.py开发中使用Formalchemy进行服务端表单数据验证的方法。以User表单为例,详细说明了对各字段的验证要求,包括必填、长度限制、唯一性等。同时介绍了如何自定义验证方法来实现验证唯一性和两个密码是否相等的功能。该文提供了相关代码示例。 ... [详细]
  • 基于PgpoolII的PostgreSQL集群安装与配置教程
    本文介绍了基于PgpoolII的PostgreSQL集群的安装与配置教程。Pgpool-II是一个位于PostgreSQL服务器和PostgreSQL数据库客户端之间的中间件,提供了连接池、复制、负载均衡、缓存、看门狗、限制链接等功能,可以用于搭建高可用的PostgreSQL集群。文章详细介绍了通过yum安装Pgpool-II的步骤,并提供了相关的官方参考地址。 ... [详细]
  • r2dbc配置多数据源
    R2dbc配置多数据源问题根据官网配置r2dbc连接mysql多数据源所遇到的问题pom配置可以参考官网,不过我这样配置会报错我并没有这样配置将以下内容添加到pom.xml文件d ... [详细]
  • MySQL数据库锁机制及其应用(数据库锁的概念)
    本文介绍了MySQL数据库锁机制及其应用。数据库锁是计算机协调多个进程或线程并发访问某一资源的机制,在数据库中,数据是一种供许多用户共享的资源,如何保证数据并发访问的一致性和有效性是数据库必须解决的问题。MySQL的锁机制相对简单,不同的存储引擎支持不同的锁机制,主要包括表级锁、行级锁和页面锁。本文详细介绍了MySQL表级锁的锁模式和特点,以及行级锁和页面锁的特点和应用场景。同时还讨论了锁冲突对数据库并发访问性能的影响。 ... [详细]
  • 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之六 || API项目整体搭建 6.1 仓储模式
    代码已上传Github+Gitee,文末有地址  书接上文:前几回文章中,我们花了三天的时间简单了解了下接口文档Swagger框架,已经完全解放了我们的以前的Word说明文档,并且可以在线进行调 ... [详细]
  • Imtryingtofigureoutawaytogeneratetorrentfilesfromabucket,usingtheAWSSDKforGo.我正 ... [详细]
  • Spring学习(4):Spring管理对象之间的关联关系
    本文是关于Spring学习的第四篇文章,讲述了Spring框架中管理对象之间的关联关系。文章介绍了MessageService类和MessagePrinter类的实现,并解释了它们之间的关联关系。通过学习本文,读者可以了解Spring框架中对象之间的关联关系的概念和实现方式。 ... [详细]
  • 本文介绍了OpenStack的逻辑概念以及其构成简介,包括了软件开源项目、基础设施资源管理平台、三大核心组件等内容。同时还介绍了Horizon(UI模块)等相关信息。 ... [详细]
  • 本文记录了在vue cli 3.x中移除console的一些采坑经验,通过使用uglifyjs-webpack-plugin插件,在vue.config.js中进行相关配置,包括设置minimizer、UglifyJsPlugin和compress等参数,最终成功移除了console。同时,还包括了一些可能出现的报错情况和解决方法。 ... [详细]
  • 本文主要复习了数据库的一些知识点,包括环境变量设置、表之间的引用关系等。同时介绍了一些常用的数据库命令及其使用方法,如创建数据库、查看已存在的数据库、切换数据库、创建表等操作。通过本文的学习,可以加深对数据库的理解和应用能力。 ... [详细]
  • 在Oracle11g以前版本中的的DataGuard物理备用数据库,可以以只读的方式打开数据库,但此时MediaRecovery利用日志进行数据同步的过 ... [详细]
  • MVC设计模式的介绍和演化过程
    本文介绍了MVC设计模式的基本概念和原理,以及在实际项目中的演化过程。通过分离视图、模型和控制器,实现了代码的解耦和重用,提高了项目的可维护性和可扩展性。详细讲解了分离视图、分离模型和分离控制器的具体步骤和规则,以及它们在项目中的应用。同时,还介绍了基础模型的封装和控制器的命名规则。该文章适合对MVC设计模式感兴趣的读者阅读和学习。 ... [详细]
  • 本文介绍了MVP架构模式及其在国庆技术博客中的应用。MVP架构模式是一种演变自MVC架构的新模式,其中View和Model之间的通信通过Presenter进行。相比MVC架构,MVP架构将交互逻辑放在Presenter内部,而View直接从Model中读取数据而不是通过Controller。本文还探讨了MVP架构在国庆技术博客中的具体应用。 ... [详细]
  • centos安装Mysql的方法及步骤详解
    本文介绍了centos安装Mysql的两种方式:rpm方式和绿色方式安装,详细介绍了安装所需的软件包以及安装过程中的注意事项,包括检查是否安装成功的方法。通过本文,读者可以了解到在centos系统上如何正确安装Mysql。 ... [详细]
  • 本文介绍了如何在Azure应用服务实例上获取.NetCore 3.0+的支持。作者分享了自己在将代码升级为使用.NET Core 3.0时遇到的问题,并提供了解决方法。文章还介绍了在部署过程中使用Kudu构建的方法,并指出了可能出现的错误。此外,还介绍了开发者应用服务计划和免费产品应用服务计划在不同地区的运行情况。最后,文章指出了当前的.NET SDK不支持目标为.NET Core 3.0的问题,并提供了解决方案。 ... [详细]
author-avatar
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有