1、IoC/DI简介
IoC 即 Inversion of Control,DI 即 Dependency Injection,前一个中文含义为控制反转,后一个译为依赖注入,可以理解成一种编程模式,详细的说明可参见大牛Martin Fowler的强文 http://martinfowler.com/articles/injection.html,借用Hollywood的名言:Don't call us, we'll call you,意即你呆着别动,到时我会找你。控制反转的核心是控制权的转移,从原有的应用程序转移到框架如IoC容器,从而实现模块间的解耦。
2、Unity是什么?
Unity是微软Patterns & Practices团队所开发的一个轻量级的,并且可扩展的依赖注入(Dependency Injection)容器,它支持常用的三种依赖注入方式:构造器注入(Constructor Injection)、属性注入(Property Injection),以及方法调用注入(Method Call Injection)。现在Unity最新的版本的3.5版,可以在微软的开源站点:https://github.com/unitycontainer/unity下载最新的发布版本和文档。
它有助于构建松耦合的应用程序和为开发者提供以下便利:
- 简化对象的创建,特别在分层对象结构和依赖的情形下;
- 它支持需求的抽象化,这允许开发人员在运行时或在配置文件中指定依赖,简化横切关注点(crosscutting concerns)的管理;
- 它通过把组件配置推给容器来决定,增加了灵活性;
- 服务定位能力; 这使客户端能够存储或缓存容器;
- 轻松构建松耦合结构的程序,从而让整个程序框架变得清晰和易于维护。
3、如何使用Unity?
下面我们用一个简单的例子来演示如何使用Ioc框架:Unity。我们的大多数应用程序都是由两个或是更多的类通过彼此的合作来实现业务逻辑,这使得每个对象都需要获取与其合作的对象(也就是它所依赖的对象)的引用。如果这个获取过程要靠自身实现,那么这将导致代码高度耦合并且难以维护和调试。使用Unity就是要解决这个这个问题。
Martin Fowler在那篇著名的文章《Inversion of Control Containers and the Dependency Injection pattern》中将具体依赖注入划分为三种形式,即构造器注入、属性(设置)注入和接口注入,习惯将其划分为一种(类型)匹配和三种注入:
- 类型匹配(Type Matching):虽然我们通过接口(或者抽象类)来进行服务调用,但是服务本身还是实现在某个具体的服务类型中,这就需要某个类型注册机制来解决服务接口和服务实现类型之间的匹配关系;
- 构造器注入(Constructor Injection):IoC容器会智能选择选择和调用适合的构造函数以创建依赖的对象。如果被选择的构造函数具有相应的参数,IoC容器在调用构造函数之前解析注册的依赖关系并自行获得相应参数对象;
- 属性注入(Property Injection):如果需要使用到被依赖对象的某个属性,在被依赖对象被创建之后,IoC容器会自动初始化该属性;
- 方法注入(Method Injection):如果被依赖对象需要调用某个方法进行相应的初始化,在该对象创建之后,IoC容器会自动调用该方法。
依赖翻转的核心原则:
1、高层模块不应该依赖底层模块,两个都应该依赖抽象(抽象类或接口);
2、抽象不应该依赖细节,细节应该依赖抽象。
下面我采用“属性注入(Property Injection)”的方式演示如何使用Unity框架。
3.1、定义接口类
一个简单日志记录接口类,用于记录请求的报文。
public interface ILogger
{
Tuple<string, string> GetLogContent(AtomRequest request, AtomResponse response = null);
void Log(Tuple<string, string> logInfo);
}
3.2、定义接口的实现类
定义2个接口的实现类,分别用来实现对请求的响应的报文进行日志记录。
internal class LogRequest : ILogger
{
public Tuple<string, string> GetLogContent(AtomRequest request, AtomResponse response = null)
{
var reqType = request.GetType().Name;
switch (reqType)
{
case "AtomQueryRequest":
{
var req = (AtomQueryRequest) request;
return Tuple.Create(request.CollectionId, string.Format("流水号:{0} 交易类型:{1}", req.TransNo, req.TransType));
}
case "AtomSaleRequest":
{
var req = (AtomSaleRequest) request;
return Tuple.Create(req.CollectionId, string.Format("流水号:{0} 订单号:{1} 金额:{2}", req.TransNo, req.OrderNo, req.LocalAmount));
}
default:
{
throw new CheckRequestException("无效的交易类型:".Contact(reqType));
}
}
}
public void Log(Tuple<string, string> logInfo)
{
LogManager.InfoRequest(logInfo.Item1, logInfo.Item2);
}
}
3.3、定义容器并注册接口及接口实现类之间的映射关系
项目引用:Microsoft.Practices.Unity.dll
internal class ServiceContainer
{
// 核心容器类型的定义,设置为静态的,公开的,可为其它任何类型调用。
public static UnityContainer RtpContainer;
// 初始化容器并在容器中注册项目程序中所有的依赖关系
static ServiceContainer()
{
RtpContainer = new UnityContainer();
// 每次调用,容器都会生成一个新的对象实例
RtpContainer.RegisterType<IResponseProcessor, ResponseProcessor>();
// 注册为单例,任何时候调用都使用同一个对象实例
RtpContainer.RegisterType<ILogger, LogRequest>("Request", new ContainerCont