三分钟搞懂依赖注入
应用程序通常是由多个组件构成的,组件和组件之间往往存在直接依赖的关系。这种依赖关系看似稳定,但是耦合度很高,如果其中一方不存在的话另一方也就无法工作,而且这种关系也增加了程序的维护成本和灵活性,同时也增加了单元测试的难度。那么,如何解决这个问题呢?这里我们就引入了依赖注入,下面我们通过一个例子来一步一步讲解。 例子很简单,下面这段代码将会向控制台输出一段话,这段话是从 Service 服务中获取的。
public class Demo
{
public void ShowMessage()
{
DataService dataService = new DataService();
string message = dataService.getMessage();
Console.Write(message);
}
}
在不使用依赖注入的情况下,我们通常就会上面这样编写代码,这段代码就像我前面所说的那样,如果 DataService 不存在或者 getMessage 不存在,那么这段代码是无法运行和编译的。下面我们就使用依赖注入的方式来解决这个问题。依赖注入的核心原则是高层不应直接依赖底层,两者均应依赖抽象或接口。根据这个核心原则我们分析前面的代码可知 ShowMessage (高层) 依赖于 DataService (底层),这时我们就需要将 DataService 替换成接口,高层将不会依赖于底层而是依赖于接口,此时高层只关心接口,而不去关心具体的实现,这就形成了 依赖倒置 。基于此,我们定义一个 IDataService 接口,然后 DataService 来实现这个接口。
public interface IDataService
{
string getMessage();
}
public class DataService : IDataService
{
public string getMessage()
{
return "Hi, I am Jack!";
}
}
接下来我们在 Demo 中使用依赖注入:
public class Demo
{
private readonly IDataService dataService;
public Demo(IDataService dataService)
{
this.dataService = dataService;
}
public void ShowMessage()
{
string message = dataService.getMessage();
Console.Write(message);
}
}
我们在上面的代码中通过构造函数向 Demo 中注入了所需的依赖,这种通过构造方法注入依赖的方式称为 构造函数注入 ,是最常见的方式。通过构造函数获得需要依赖是类级别的全局变量,可以在整个类内部使用,并且构造函数注入遵循了显示依赖原则。除了构造函数注入外,还存在另外两种注入方式:属性注入和方法注入。属性注入是通过设置类属性来获取所需的依赖,属性注入虽然看起来和构造函数注入类似,但是它不需要显示的提供依赖,只要在已经实例化的对象上设置相应的属性即可,但是这样做会存在问题,因为依赖项属性设置的值不是强制性的,因此很容易忘记设置从而引发异常。方法注入则是通过在方法的参数中传入所需的依赖,需要注意的是这个方法必须是 public 类型。 当程序中有很多个地方需要用到依赖注入时,我们就需要一个类来专门负责管理创建所需的类,并创建它所有可能用到的依赖,这个类就是依赖注入容器。我们可以把依赖注入容器看作一个创建对象的工厂,负责向外提供被请求要创建的对象,同时提供对象生命周期的管理。
- 剑指OFFER之数值的整数次方(九度OJ1514)
- 剑指OFFER之变态跳台阶(九度OJ1389)
- 剑指OFFER之二进制中1的个数(九度OJ1513)
- 剑指OFFER之跳台阶(九度OJ1388)
- 剑指OFFER之二维数组中的查找(九度OJ1384)
- 剑指OFFER之用两个栈实现队列(九度OJ1512)
- 剑指OFFER之用两个栈实现队列(九度OJ1512)
- 简单的客户机服务器投射模拟
- 使用gcc编译gdb调试
- 剑指OFFER之第一个只出现一次的字符(九度OJ1283)
- c++中类长度解析
- 剑指OFFER之丑数(九度OJ1214)
- 剑指OFFER之把数组排成最小的数(九度OJ1504)
- 剑指OFFER之从1到n中出现1的次数(九度OJ1373)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- python api链接数据库
- MySQL 的可重复读
- 第四天:创建型模式--原型模式
- Ubuntu16升级Python3
- JavaScript 进阶教程(3)---让你彻底搞懂原型链和继承
- 第五天:结构型模式--适配器模式
- 第六天:结构型模式--修饰器模式
- 第一章--第二节:我的第一个Python项目
- 第二章--第一节:变量、字符串与数字
- 第二章--第二节:注释
- 骚操作 | 用 Python 实现 GIF 倒放
- TensorFlow学习笔记--CIFAR-10 图像识别
- TensorFlow学习笔记--自定义图像识别
- TensorFlow学习笔记--Deep Dream模型
- Python入门系列第二章--第一节:变量、字符串与数字