解构控制反转(IoC)和依赖注入(DI)
1.控制反转
@H_301_36@控制反转(Inversion of Control@H_301_36@,IoC@H_301_36@),简言之就是代码的控制器交由系统控制,而不是在代码内部,通过IoC@H_301_36@,消除组件或者模块间的直接依赖,使得软件系统的开发更具柔性和扩展性。控制反转的典型应用体现在框架系统的设计上,是框架系统的基本特征,不管是.NET Framework@H_301_36@抑或是Java Framework@H_301_36@都是建立在控制反转的思想基础之上。
@H_301_36@控制反转很多时候被看做是依赖倒置原则的一个同义词,其概念产生的背景大概来源于框架系统的设计,例如.NET Framework@H_301_36@就是一个庞大的框架(Framework@H_301_36@)系统。在.NET Framework@H_301_36@大平台上可以很容易地构建ASP.NET Web@H_301_36@应用、Silverlight@H_301_36@应用、Windows Phone@H_301_36@应用或者Window Azure Cloud@H_301_36@应用。很多时候,基于.NET Framework@H_301_36@构建自定义系统的方式就是对.NET Framework@H_301_36@本身的扩展,调用框架提供的基础API@H_301_36@,扩展自定义的系统功能和行为。然而,不管如何新建或者扩展自定义功能,代码执行的最终控制权还是回到框架中执行,再交回应用程序。黄忠诚先生曾经在Object Builder Application Block@H_301_36@一文中给出一个较为贴切的举例,就是在Window From@H_301_36@应用程序中,当Application.Run@H_301_36@调用之后,程序的控制权交由Windows Froms Framework@H_301_36@上。所以,控制反转更强调控制权的反转,体现了控制流程的依赖倒置,所以从这个意义上来说,控制反转是依赖倒置的特例。
2.依赖注入
@H_301_36@依赖注入(Dependency Injection@H_301_36@,DI@H_301_36@),早见于Martin Flower@H_301_36@的Inversion of Control Containers and the Dependency Injection pattern@H_301_36@一文,其定义可概括为:
@H_301_36@客户类依赖于服务类的抽象接口,并在运行时根据上下文环境,由其他组件(例如DI@H_301_36@容器)实例化具体的服务类实例,将其注入到客户类的运行时环境,实现客户类与服务类实例之间松散 @H_301_36@的耦合关系。
(1)常见的三种注入方式
@H_301_36@简单而言,依赖注入的方式被总结为以下三种。
· @H_301_36@接口注入(Interface Injection@H_301_36@),将对象间的关系转移到一个接口,以接口注入控制。
@H_301_36@首先定义注入的接口:
publicinterfaceIRunnerProvider
{
voidRun(Actionaction);
}
@H_301_36@为注入的接口实现不同环境下的注入提供器,本例的系统是一个后台处理程序提供了运行环境的多种可能,默认情况下将运行于单独的线程,或者通过独立的Windows Service@H_301_36@进程运行,那么需要为不同的情况实现不同的提供器,例如:
publicclassDefaultRunnerProvider:IRunnerProvider
{
#regionIRunnerProviderMembers
publicvoidRun(Actionaction)
{
varthread=newThread(()=>action());
thread.Start();
}
#endregion
}
@H_301_36@对于后台服务的Host@H_301_36@类,通过配置获取注入的接口实例,而Run@H_301_36@方法的执行过程则被注入了接口所定义的逻辑,该逻辑由上下文配置所定义:
publicclassRunnerHost:IDisposable
{
IRunnerProviderprovider=null;
publicRunnerHost()
{
//Get Provider by configuration
provider=GetProvider(config.Host.Provider.Name);
}
publicvoidRun()
{
if(provider!=null)
{
provider.Run(()=>
{
//exceute logic in this provider,if provider is DefualtRunnerProvider,
//then this logic will run in a new thread context.
});
}
}
}
@H_301_36@接口注入,为无须重新编译即可修改注入逻辑提供了可能,GetProvider方法完全可以通过读取配置文件的config.Host.Provider.Name内容,来动态地创建对应的Provider,从而动态地改变BackgroundHost的Run()行为。
· @H_301_36@构造器注入(Constructor Injection@H_301_36@),客户类在类型构造时,将服务类实例以构造函数参数的形式传递给客户端,因此服务类实例一旦注入将不可修改。
publicclassPicWorker
{
}
publicclassPicClient
{
privatePicWorkerworker;
publicPicClient(PicWorkerworker)
{
//通过构造器注入
this.worker=worker;
}
}
· @H_301_36@属性注入(Setter Injection@H_301_36@),通过客户类属性设置的方式,将服务器类实例在运行时设定为客户类属性,相较构造器注入方式,属性注入提供了改写服务器类实例的可能。
publicclassPicClient
{
privatePicWorkerworker;
//通过属性注入
publicPicWorkerWoker
{
get{returnthis.worker;}
set{this.worker=value;}
}
}
@H_301_36@另外,在.NET@H_301_36@平台下,除了Martin Flower@H_301_36@大师提出的三种注入方式之外,还有一种更优雅的选择,那就是依靠.NET@H_301_36@特有的Attribute@H_301_36@实现,以ASP .NET MVC@H_301_36@中的Action Filter@H_301_36@为例:
[HttpPost]
publicActionResultRegister(RegisterModelmodel)
{
//省略注册过程
returnView(model);
}
@H_301_36@其中,HttpPostAttribute@H_301_36@就是通过Attribute@H_301_36@方式为Register Action@H_301_36@注入了自动检查Post@H_301_36@请求的逻辑,同样的注入方式广泛存在于ASP .NET MVC@H_301_36@的很多Filter@H_301_36@逻辑中。
[AttributeUsage(AttributeTargets.Method,AllowMultiple=false,Inherited=true)]
publicsealedclassHttpPostAttribute:ActionMethodSelectorAttribute
{
//Fields
privatestaticreadonlyAcceptVerbsAttribute_innerAttribute=newAcceptVerbsAttribute(HttpVerbs.Post);
@H_626_1301@
//Methods
publicoverrideboolIsValidForRequest(ControllerContextcontrollerContext,MethodInfomethodInfo)
{
return_innerAttribute.IsValidForRequest(controllerContext,methodInfo);
}
}
@H_301_36@关于Attribute@H_301_36@的详细内容,请参考8.3@H_301_36@节“历史纠葛:特性和属性”,其中的TrimAttribute@H_301_36@特性正是应用Attribute@H_301_36@注入进行属性Trim@H_301_36@过滤处理的典型应用。
本文节选自《你必须知道的.NET(第2版)》一书
图书详细信息:http://bvbroadview.blog.51cto.com/3227029/635423