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. 两套JRE

    JDK就是Java Development Kit.简单的说JDK是面向开发人员使用的SDK,它提供了Java的开发环境和运行环境. SDK是Software Development Kit 一般指软 ...

  2. 【WP 8.1开发】手机客户端应用接收推送通知

    上一篇文章中,已经完成了用于发送通知的服务器端,接下来我们就用这个服务端来测试一下. 在开始测试之前,我们要做一个接收通知的WP应用. 1.启动VS Express for Windows,新建项目, ...

  3. JS的IE和Firefox兼容性

    以下以 IE 代替 Internet Explorer,以 MF 代替 Mozzila Firefox 0.px的问题 obj.style.left = ”100px”; obj.style.top ...

  4. javascript类型系统——布尔Boolean类型

    × 目录 [1]定义 [2]应用场景 [3]转为布尔[4]实例方法 前面的话 布尔值Boolean类型可能是三种包装对象Number.String和Boolean中最简单的一种.Number和Stri ...

  5. hdu 1241 Oil Deposits (一次dfs搞定有某有)

    #include<iostream> #include<cstring> #include<cstdio> #include<algorithm> us ...

  6. linux命令分享(四):iostat

    简介 iostat主要用于监控系统设备的IO负载情况,iostat首次运行时显示自系统启动开始的各项统计信息,之后运行iostat将显示自上次运行该命令以后的统计信息.用户可以通过指定统计的次数和时间 ...

  7. selenium-webdriver(python) (十五) -- 鼠标事件

    本节重点: ActionChains 类 context_click()  右击 double_click()   双击 drag_and_drop()  拖动 测试的产品中有一个操作是右键点击文件列 ...

  8. Struts2整合Hibernate3实现用户登录功能

    所用技术:struts2 ,hibernate,jsp,mysql 本DEMO仅仅实现用户登录功能,采用MVC思想,自己也觉得相对是比较简单,比较容易理解数据流向的一个例子,通过整合这个过程,能够清晰 ...

  9. JS的一些日期操作

    以下语句,作者都亲自整理,并调试过,转载请注明出处 var nowDate = new Date(); nowDate.getYear(); //获取当前年份(2位) nowDate.getFullY ...

  10. .NET平台下IIS7.5+无后缀名伪静态实现办法

    首先新建一个应用程序池,名称任意,比如:nettest,托管管道模式先暂时设置为集成模式,等下面的一系列设置完成之后再设置成经典模式: 部署好站点,并将此站点的应用程序池设置为nettest; 选中站 ...