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. js算法之最常用的排序

    引入 大学学习计算机语言的那几年,从c语言,到c++,再到数据结构JAVA..让我印象最深刻的还是最开始老师讲冒泡算法的时候,直到现在大四快毕业了我才渐渐通窍了.刚学前端的时候以为前端就是做出好看很炫 ...

  2. Comet服务器推送与SignalR

        HTTP协议是一个典型的Request/Response协议,是基于TCP/IP之上的一个应用层协议,该协议最典型的特点就是无状态且需要客户端发起Request服务端才能进行Response, ...

  3. WPF自定义控件与样式(6)-ScrollViewer与ListBox自定义样式

    一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接. 本文主要内容: Scr ...

  4. javascript类型系统——数组array

    × 目录 [1]创建 [2]本质 [3]稀疏[4]长度[5]遍历[6]类数组 前面的话 除了对象之外,数组Array类型可能是javascript中最常用的类型了.而且,javascript中的数组与 ...

  5. CentOS yum安装Apache + PHP + Tomcat7 + MySQL

    Linux平台上用得最多的web环境就是php.java和MySQL了,会搭建这个环境,就能把很多开源程序跑起来. 作为一个程序猿,虽然并不用精通运维的活,但基本的Linux环境搭建还是要掌握比较好, ...

  6. SQLServer学习笔记系列10

    一.写在前面的话 生活的路很长,还是要坚持走下去,自己选择的生活,就该让这样的生活放射精彩!我不奢求现在的积累,在将来能够收获多少,至少在以后的日子里回忆起来,我不曾放弃过,我坚持过,我不后悔!最近跟 ...

  7. MAPPING SEGMENTS TO PAGES

    The segmentation and paging mechanisms provide in the support a wide variety of approaches to memory ...

  8. CentOS 7添加yum源

    RedHat下主要有两个distribution,一曰 RHEL(Red Hat Enterprise Linux) 一曰 Fedora.前者主要是供服务器使用,优点是稳定,缺点是老的掉牙,后者是供桌 ...

  9. Azure Automation (1) 入门

    <Windows Azure Platform 系列文章目录> 通过Azure Automation(自动化),开发人员可以自动完成通常要在云环境中执行的手动.长时间进行.易出错且重复性高 ...

  10. Elasticsearch——分页查询From&Size VS scroll

    Elasticsearch中数据都存储在分片中,当执行搜索时每个分片独立搜索后,数据再经过整合返回.那么,如果要实现分页查询该怎么办呢? 更多内容参考Elasticsearch资料汇总 按照一般的查询 ...