依赖注入和控制反转到底是什么意思?
控制反转(Ioc):调用者不再创建被调用者的实例,由IOC容器框架创建(C#中常用的IOC框架有Autofac/Unity等等),这种方式称为控制反转
依赖注入(DI):容器框架将创建好的实例注入到调用者称为依赖注入
依赖倒置原则(DIP)
高层模块不应依赖于低层模块,两者应该依赖于抽象。
抽象不不应该依赖于实现,实现应该依赖于抽象。
使用Ioc的好处是什么?
控制反转是面向对象编程中的一种设计原则,可以用来降低代码之间的耦合度,Ioc把创建和查找依赖对象的控制权交给容器,由容器进行注入,所以对象与对象之间是松散耦合,传统的应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间是紧耦合
举例
class SqlDal { public void Add() { Console.WriteLine("向SQLServer数据库添加一条新订单"); } } class Order { SqlDal sqlDal = new SqlDal(); public void Add() { sqlDal.Add(); } }
首先定义了SqlDal类用于SqlServer数据库的读写操作,再定义Order类负责订单的逻辑处理,因为订单数据是要插入到数据库的,所以在Order类中定了SqlDal类的变量并初始化.这里Order是依赖于SqlDal类的
控制台调用
static void Main(string[] args) { Order order = new Order(); order.Add(); Console.ReadKey(); }
这是传统开发中比较常见的方式,如果这时候数据库改为了mysql,那么SqlDal是无法继续使用了,需要重新定义mysqlDal,负责mysql数据库的读写操作
class MySqlDal { public void Add() { Console.WriteLine("向MySql数据库添加一条新订单"); } }
由于Order类中直接引用了SqlDal类的对象,所以也需要同步修改为MySqlDal
class Order { MySqlDal mySqlDal = new MySqlDal(); public void Add() { mySqlDal.Add(); } }
OK,可以满足当前需求了,但是如果后期类似的情况再次出现,就意味着需要将刚才的操作重新做一次,显然这不是一个良好的设计,类与类之间是高度耦合的,可扩展性较差,它违背了DIP原则,高层模块Order不应依赖于底层模块SqlDal、MySqlDal,两者应该依赖于抽象
改写代码
public interface IDal { void Add(); } class SqlDal : IDal { public void Add() { Console.WriteLine("向SQLServer数据库添加一条新订单"); } } class Order { private IDal _dal; /// <summary> /// 构造函数注入 /// </summary> /// <param name="dal"></param> public Order(IDal dal) { _dal = dal; } public void Add() { _dal.Add(); } }
第一步:定义接口IDal,添加方法Add()
第二步:定义SqlDal继承IDal接口并实现接口中的Add()方法
第三步:在Order类中添加私有变量用于存储抽象并在构造函数中传递具体依赖对象
控制台调用
static void Main(string[] args) { Order order = new Order(new SqlDal()); order.Add(); Console.ReadKey(); }
class MySqlDal : IDal { public void Add() { Console.WriteLine("向MySql数据库添加一条新订单"); } }
Order order = new Order(new MySqlDal()); order.Add();
输出结果是一样的,但是这里将依赖对象SqlDal对象的创建和绑定转移到了Order外部来实现,这就解除了Order与SqlDal类的耦合关系,如果现在出现需要更改数据库为MySql的需求,只需要定义MySqlDal类继承IDal并实现Add()方法,然后外部绑定依赖,不需要修改Order类内部的代码即可实现替换,很明显这种方式程序的可扩展性更高
属性注入
上面所示的方式是在Order类的构造函数中注入依赖对象,还可以使用属性注入的方式,顾名思义,属性注入是通过属性来传递依赖,因此在Order类中定义一个属性
class Order { private IDal _dal; /// <summary> /// 属性注入 /// </summary> public IDal Dal { get { return _dal; } set { _dal = value; } }
public void Add() { _dal.Add(); } }
在控制台调用时给属性赋值从而传递依赖
static void Main(string[] args) { Order order = new Order(); order.Dal = new SqlDal(); order.Add(); Console.ReadKey(); }
接口注入
相比构造函数注入和属性注入,接口注入显得有些复杂,使用也不常见。具体思路是先定义一个接口,包含一个设置依赖的方法。然后依赖类,继承并实现这个接口
public interface IDependent { void SetDepend(IDal idal); }
class Order: IDependent { private IDal _dal; public void Add() { _dal.Add(); } public void SetDepend(IDal idal) { _dal = idal; } }
通过SetDepend方法传递依赖
static void Main(string[] args) { Order order = new Order(); order.SetDepend(new SqlDal()); order.Add(); Console.ReadKey(); }
Ioc容器
前面所有的例子中,我们都是通过手动的方式来创建依赖对象,并将引用传递给被依赖模块,对于大型项目来说,相互依赖的组件比较多。如果还用手动的方式自己来创建和注入依赖的话,显然效率很低,而且往往还会出现不可控的场面。正因如此,Ioc容器诞生了。Ioc容器实际上是一个DI框架,它能简化我们的工作量。它包含以下几个功能
总结:DIP是软件设计的一种思想,Ioc则是基于DIP衍生出的一种软件设计模式。DI是Ioc的具体实现方式之一,使用最为广泛。Ioc容器是DI构造函注入的框架,它管理着依赖项的生命周期以及映射关系
下面主要学习一下Ioc框架中的Autofac的使用
首先在项目中使用nuget安装Autofac
原文:https://www.cnblogs.com/GnailGnepGnaw/p/10743235.html