1 概述

当我们想闭上眼睛想如何让我们的软件更加可用可维护时,我们总能想到一个词:松耦合。在这篇文章中,主要讲述了模块间存在的依赖关系,但这种依赖关系违背了依赖倒置原则。在这之后,我们将讨论一种解除软件依赖关系的设计模式——IoC,以及它的两种实现方法:依赖注入(DI)和服务定位。最后我们简单地列下当前流行的IoC容器工具。

目录

  • 依赖
  • 依赖倒置原则(DIP)
  • 控制反转IoC:解除两个模块间的直接依赖关系
  • 依赖注入(DI)
  • 服务定位(Service Locator)
  • IoC容器

2 依赖

当一个模块/类使用另一个模块/类,即存在了一种依赖关系。

aaarticlea/png;base64,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" alt="" />

示例场景:

在这里,我们使用销售系统中保存订单的场景。比如,我们当前的需求是将数据保存到SQL Server中。在这里我们只关注业务层与数据持久层之间的依赖关系。

示例v1:

    public class SQLServerOrderDAL
{
public void Add()
{
Console.WriteLine("The order was added in into sql server database.");
}
} public class OrderManager
{
public void AddOrder()
{
SQLServerOrderDAL orderDAL = new SQLServerOrderDAL();
orderDAL.Add();
}
} class Program
{
static void Main(string[] args)
{
OrderManager orderManager = new OrderManager();
orderManager.AddOrder(); Console.ReadLine();
}
}

3 依赖倒置原则(DIP)

在这个时候,当公司的牛XX业务人员将这个销售系统又卖给了另一家公司,大家都HAPPY。 但是对方公司唯一希望数据保存到ORACLE,因为他们其它的软件已经购买了ORACLE授权,并且不想再购买SQL SERVER授权。即软件需要支持数据库更换,我们应该如何解决

依赖倒置原则(DIP)

高层模块不应依赖于低层模块,两者应该依赖于抽象。

抽象不不应该依赖于实现,实现应该依赖于抽象。

上述示例v1中,OrderManager依赖于OrderDAL. 即高层模块(OrderManager)直接依赖于实现(OrderDAL), 而不是依赖于抽象。下面我们为数据持久层添加一层抽象,让业务层和数据持久实现层都依赖于抽象。

示例v2:

    public interface IOrderDAL
{
void Add();
} public class OracleOrderDAL:IOrderDAL
{
public void Add()
{
Console.WriteLine("The order was added in into oracle database.");
}
} public class SQLServerOrderDAL:IOrderDAL
{
public void Add()
{
Console.WriteLine("The order was added in into sql server database.");
}
} public class OrderManager
{
public void AddOrder()
{
IOrderDAL orderDAL = new OracleOrderDAL();
orderDAL.Add();
}
} class Program
{
static void Main(string[] args)
{
OrderManager orderManager = new OrderManager();
orderManager.AddOrder(); Console.ReadLine();
}
}

4 控制反转IoC

程序到这里,数据访问层已经添加一层抽象,即业务层和数据持久层都依赖于抽象。但是,注意这里,依然存在都依赖,即业务层依然要直接依赖于低层的数据持久实现层。当更换数据库时,会有很多这样的代码要更换。你知道我在说什么的..这还是太恶心…

文章到这里了,我们先说说控制反转——IoC(Inversion of Control), 网上关于IoC的解释很多,我比较喜欢这种http://www.cnblogs.com/liuhaorain/p/3747470.html:

它为相互依赖的组件提供抽象,将依赖(低层模块)对象的获得交给第三方(系统)来控制即依赖对象不在被依赖模块的类中直接通过new来获取

在多数情况下,上面的解释已经够用了,更广义一些的解释我认为是:

相对于过程式编程中,代码按顺序一步步执行,控制反转强调的是,将控制权交出,让第三方来控制程序的执行。

即除了将对象创建的工作交给第三方(我们主要关心的,如依赖注入和服务定位),IoC应该还包括将流程的控制转交出去,如事件,Callback 委托,观察者模式,异步等

在接下来的文章中,我们主要讨论依赖注入和服务定位。

5 依赖注入(DI)

依赖注入DI——Dependence Injection, 是一种IoC的实现方式。即将依赖对象的创建和绑定工作转移到第三者(调用方)。即 如果A对象依赖于B或C对象,那么就将B或C对象的创建工作转到A对象的调用方去。在上面示例中,将OracleOrderDAL或SQLServerOrderDAL的创建工作和绑定到IOrderDAL的工作转移到OrderManager的调用方中去,即具体对象创建的选择权移交到更高层的调用方法中。

依赖注入又分构造函数注入,属性注入和接口注入。我认为接口注入并不好用,所以这里只做构造函数和属性注入的示例。

5.1 构造函数注入

就是通过构造函数,传递依赖项。在这里,通过OrderManager的构造函数传入SQLServerOrderDAL或OracleOrderDAL

示例v3

    public class OrderManager
{
private IOrderDAL _orderDAL; public OrderManager(IOrderDAL orderDAL)
{
this._orderDAL = orderDAL;
} public void AddOrder()
{ this._orderDAL.Add();
}
} class Program
{
static void Main(string[] args)
{
IOrderDAL orderDAL = new SQLServerOrderDAL();
//IOrderDAL orderDAL = new OracleOrderDAL(); OrderManager orderManager = new OrderManager(orderDAL);
orderManager.AddOrder(); Console.ReadLine();
}
}

5.2 属性注入

即通过属性传入依赖项。在这里,能过OrderManager的OrderDA属性项,传入SQLServerOrderDAL或OracleOrderDAL

示例4

    public class OrderManager
{
private IOrderDAL _orderDAL; public IOrderDAL OrderDAL
{
set { this._orderDAL = value; }
get { return this._orderDAL; }
} public void AddOrder()
{ this._orderDAL.Add();
}
} class Program
{
static void Main(string[] args)
{
IOrderDAL orderDAL = new SQLServerOrderDAL();
//IOrderDAL orderDAL = new OracleOrderDAL(); OrderManager orderManager = new OrderManager();
orderManager.OrderDAL = orderDAL;
orderManager.AddOrder(); Console.ReadLine();
}
}

6 服务定位(Service Locator)

服务定位SL——Service Locator, 也是一种IoC的实现方式。即将依赖对象的创建和绑定工作转移到第三者(另一个模块)。即 如果A对象依赖于B或C对象,那么就将B或C对象的创建工作转到F对象中去。在上面示例中,将OracleOrderDAL或SQLServerOrderDAL的创建工作和绑定到IOrderDAL的工作转移到Factory中去, 即具体对象的创建选择权移交给Factory中。

示例v5

    public class Factory
{
public static IOrderDAL GetOrderDAL()
{
//string type = "SQLServer";
string type = "Oracle";
string assemblyName = string.Format("v5_{0}DAL",type);
string typeName = string.Format("{0}.{1}OrderDAL",assemblyName,type);
return (IOrderDAL)Assembly.Load(assemblyName).CreateInstance(typeName);
}
} public class OrderManager
{
public void AddOrder()
{
IOrderDAL orderDAL = Factory.GetOrderDAL();
orderDAL.Add();
}
} class Program
{
static void Main(string[] args)
{
OrderManager orderManager = new OrderManager();
orderManager.AddOrder(); Console.ReadLine();
}
}

7 IoC容器

下面是一些主流的IoC容器工具以供参考

1. Unity:  http://unity.codeplex.com/

2. Ninject:  http://www.ninject.org/

3. Autofac:  http://code.google.com/p/autofac/

4. Spring.NET: http://www.springframework.net/

Unity示例v6

    public class OrderManager
{
private IOrderDAL _orderDAL; public OrderManager(IOrderDAL orderDAL)
{
this._orderDAL = orderDAL;
} public void AddOrder()
{ this._orderDAL.Add();
}
} class Program
{ static void Main(string[] args)
{
UnityContainer container = new UnityContainer();
//container.RegisterType<IOrderDAL, SQLServerOrderDAL>();
container.RegisterType<IOrderDAL, OracleOrderDAL>(); OrderManager orderManager = container.Resolve<OrderManager>();
orderManager.AddOrder(); Console.ReadLine();
}
}

上面是一些我关于IoC和DI的理解,如有不对的地方,欢迎提出讨论。

相关链接:

Inversion of Control Containers and the Dependency Injection pattern

Inversion of Control and Dependency Injection: Working with Windsor Container

Dependency Injection (DI) vs. Inversion of Control (IOC)
Inversion of Control – An Introduction with Examples in .NET

Understanding Inversion of Control, Dependency Injection and Service Locator

Dependency Injection

Dependency Inversion Principle, IoC Container, and Dependency Injection

Unity Container Introduced by MSDN

IoC和DI的理解的更多相关文章

  1. 浅谈ASP.NET Core中IOC与DI的理解和使用

    说起IOC和DI,使用过ASP.NET Core的人对这两个概念一定不陌生,早前,自己也有尝试过去了解这两个东西,但是一直觉得有点很难去理解,总觉得对其还是模糊不清,所以,趁着今天有空,就去把两个概念 ...

  2. Spring 学习教程(一):浅谈对Spring IOC以及DI的理解

    一.个人对IoC(控制反转)和DI(依赖注入)的理解我们平时在开发java web程序的时候,每个对象在需要使用它的合作对象时,自己都要将它要合作对象创建出来(比如 new 对象),这个合作对象是由自 ...

  3. 对Spring中IOC和DI的理解

    前几篇讲了Spring中IOC和DI的用法,本篇应该放到三篇之前,但一直没有想到好的讲解方式,后参考https://blog.csdn.net/luoyepiaoxue2014/article/det ...

  4. 关于IOC和DI的理解

    IOC:Inversion of Control 控制反转 DI:Dependency Injection 依赖注入 控制反转,从字面意思来看,就是控制权又被动变主动,最后又变回被动. 举个例子: 你 ...

  5. IoC与DI的理解

    首先要分享的是Iteye的开涛这位技术牛人对Spring框架的IOC的理解,写得非常通俗易懂,以下内容全部来自原文,原文地址:http://jinnianshilongnian.iteye.com/b ...

  6. 重温IOC,DI的理解

    IOC和DI其实它们是同一个概念的不同角度描述 IOC强调的是程序控制对象(创建销毁),变换成了容器来控制对象(创建销毁) DI:即IoC容器帮对象找相应的依赖对象通过反射注入     从Spring ...

  7. Spring框架IOC,DI概念理解

    1.什么是框架? 框架是一种重复使用的解决方案,针对某个软件开发的问题提出的. Spring框架,它是一个大型的包含很多重复使用的某个领域的解决方案. Spring的理念:不要重复发明轮子. 2.Sp ...

  8. 谈谈对IOC及DI的理解与思考

    一.前言 在实际的开发过程中,我们经常会遇到这样的情况,在进行调试分析问题的时候,经常需要记录日志信息,这时可以采用输出到控制台. 因此,我们通常会定义一个日志类,来实现输出日志. 定义一个生成验证的 ...

  9. 框架面试题:谈谈我对Spring IOC与DI的理解

    IOC是一种叫做“控制反转”的设计思想. 1.较浅的层次——从名字上解析 “控制”就是指对 对象的创建.维护.销毁等生命周期的控制,这个过程一般是由我们的程序去主动控制的,如使用new关键字去创建一个 ...

随机推荐

  1. Java 多态——与C++的比较

    学习了Java和C++之后,由于长期不使用C++,而java的基础知识掌握不牢,现在已经搞不清java多态了.现在先来谈谈java多态,稍后有时间再更新C++的多态,并进行比较~ 一. Java的多态 ...

  2. pycharm运行脚本为何不生成测试报告?

    今日使用python+selenium编写自动化测试脚本并执行过程中,使用pycharm运行结果后发现脚本运行无报错,脚本中的操作也正常被执行,但就是没有生成测试报告. 为什么呢,为什么呢,生成测试报 ...

  3. seajs模块化开发

    seajs是一个起辅助作用的库,所以它可以更方便开发,而它可以解决以下问题: 1.命名问题,就是冲突 2.性能问题,就是只要一个功能,但却使用一个大插件中的一个小功能,所以要手动拆分出这个功能 3.j ...

  4. Geometry Curve of OpenCascade BRep

    Geometry Curve of OpenCascade BRep eryar@163.com 摘要Abstract:几何曲线是参数表示的曲线 ,在边界表示中其数据存在于BRep_TEdge中,BR ...

  5. dropdownlist同时绑定数据库和自定义内容

    在有些时候,我们需要dropdownlist里面包含“全部”这个字段,但是数据库中不存在“全部”,只存在具体的分类项. 这种情况下就需要先把数据库的数据源绑定到dropdownlist中,然后再在后面 ...

  6. IIS负载均衡(转)

    在大型Web应用系统中,由于请求的数据量过大以及并发的因素,导致Web系统会出现宕机的现象,解决这一类问题的方法我个人觉得主要在以下几个方面: 1.IIS 负载均衡. 2.数据库 负载均衡. 3.系统 ...

  7. 使用Ambari安装hadoop集群

    最近需要做些spark的工作,所以弄了几台dell7500就这么准备开始搭建集群,之前用过几台更破的台式机搭建过一次,折腾了半个月之久,终于成功搭建,这次不想走老路,所以网上查了一下,发现一个神器AM ...

  8. Unity 3D制作2D游戏的几种方法

    1.使用本身UGUI. 2.把摄像机的投影改为正交投影,不考虑Z轴. 3.使用Untiy自身的2D模式. 4.使用2D TooKit插件.

  9. Azure ARM (3) ARM支持的服务类型

    <Windows Azure Platform 系列文章目录> 我们在使用ARM创建资源的时候,首先要确认哪些Azure服务,支持ARM模式. 具体内容,我们可以参考连接:https:// ...

  10. c# 游戏策划配置工具

    该工具是提供策划配置excel数据,导出到mysql数据库,以及生成xml文件,和对应的xml解析实体类 实现了程序 excel 列名 ID =P 表示ID这列是唯一字段 =S=300 表示这列类型是 ...