c#实现依赖注入_第1页
c#实现依赖注入_第2页
c#实现依赖注入_第3页
c#实现依赖注入_第4页
c#实现依赖注入_第5页
已阅读5页,还剩21页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、c#实现依赖注入1. 问题的提出开发中,尤其是大型项目的开发中,为了降低模块间、类间的耦合关系,比较提倡基于接口开发,但在实现中也必须面临最终是 “谁” 提供实体类的问题。 martin fowler 在 inversionof control containers and the dependency injection pattern 中也提到了标准的三种实现方式 constructor injection 、setter injection 和 interface injection ,很全面的阐释了这个问题。对于c#而言,由于语法元素上本身要比java丰富,如何实施注入还有些技巧和特色

2、之处。这方面微软的 objectbuilder是个不错的教科书,对三种标准方式的实现也都很到位,但就是有些庞大了。本文中, 笔者借鉴 martin fowler 的撰文, 也通过一些精简的代码片断向读者介绍c#实现依赖注入的基本技巧。我有个习惯,每天晚上要看天气预报,就以这个开始好了,先定义待注入对象的抽象行为描述,然后增加一个假的实体类,相关代码和单元测试如下:c# using system;namespace visionlogic.training.dependencyinjection.scenario/ <summary>/ 抽象注入对象接口/ </summary&

3、gt;public interface iweatherreaderstring current get;c#using system;namespacevisionlogic.training.dependencyinjection.scenario.raw / <summary>/ 伪造的一个实现类/ </summary>class fakeweatherreader : iweatherreaderpublic string current get return string.empty; / <summary>/ 客户程序/ </summary

4、>public class clientprotected iweatherreader reader = new fakeweatherreader();public virtual string weathergetstring current = reader.current;switch (current)case s: return sunny;case r: return rainy;case c: return cloudy;default:return unknown;unit testusing microsoft.visualstudio.testtools.unit

5、testing;using visionlogic.training.dependencyinjection.scenario;using visionlogic.training.dependencyinjection.scenario.raw;namespacevisionlogic.training.dependencyinjection.scenario.unittest.r awtestclasspublic class weatherreadertesttestmethodpublic void test()client client = new client();assert.a

6、reequal<string>(unknown, client.weather);问题就出现了,虽然美好的愿望是client 仅仅依赖抽象的iweatherreader,但之前总要和一个实体类“轧” 一道,那么实际的效果就是实体类作了修改、重新编译了, client也要处理,没有真正达到隔离的目的。依赖注入通过引入第三方责任者的方法,相对好的梳理了这个关系,这位重要的角色就是一个assembler 类, 他和实体类型打交道, 对 client而言他总是可以根据约定,加工出需要的iweatherreader。2 .进一步的分析看上去, client 被解放了,但又套住了 assemb

7、ler ,为了尽量让他与实体类间松散些需要做什么呢?首先要完成自己的职责:可以找到合适的实现类实例,不管是重新构造一个还是找个现成的。既要根据需要加工接口iweatherreader, 又要让自己尽量不与大量的实体类纠缠在一起,最好的办法就是从.netframework 中再找到一个“第三方” ,这里选中了system.activator 。还有就是当客户程序调用 assembler 的时候, 它需要知道需要通过哪个实现类的实例返回,该项工作一方面可以通过一个字典完成,也可以通过配置解决,两者应用都很普遍,怎么选择呢抽象,提取一个接口,然后都实现。由于本文主要介绍依赖注入的实现,为了简单起见,

8、采用一个伪造的内存字典方式, 而非基于 system.configuration 的配置系统实现一个assembler 的协同类。c# 新增一个用于管理抽象类型实体类型映射关系的类型 itypemapusing system;using system.collections.generic;namespace visionlogic.training.dependencyinjection.scenario/ <summary>/ 考虑到某些类型没有无参的构造函数, 增加了描述构造信息的专门结构/ </summary>public class typeconstruct

9、orprivate type type;private object constructorparameters;public typeconstructor(type type, params objectconstructorparameters)this.type = type;this.constructorparameters = constructorparameters;public typeconstructor(type type) : this(type, null) public type type get return type; public object const

10、ructorparameters get returnconstructorparameters; / <summary>/ 管理抽象类型与实体类型的字典类型/ </summary>public interface itypemaptypeconstructor thistype targetget;c# 实现一个 assembler 类型, 为了示例方便, 同时实现了一个 itypemap 和 iweatherreaderusing system;using system.collections.generic;namespace visionlogic.traini

11、ng.dependencyinjection.scenario/ <summary>/ 测试用的实体类/ </summary>public class weatherreaderimpl : iweatherreaderprivate string weather;public weatherreaderimpl(string weather)this.weather = weather;public string currentget return weather; / <summary>/ 管理抽象类型与实际实体类型映射关系, 实际工程中应该从配置系统、

12、参数系统获得。/ 这里为了示例方便,采用了一个纯内存字典的方式。/ </summary>public class memorytypemap : itypemapprivate dictionary<type, typeconstructor> dictionary = new dictionary<type, typeconstructor>();public static readonly itypemap instance;/ <summary>/ singleton/ </summary>private memorytypem

13、ap()static memorytypemap()memorytypemap singleton = new memorytypemap();/ 注册抽象类型需要使用的实体类型/ 该类型实体具有构造参数,实际的配置信息可以从外层机制获得。singleton.dictionary.add(typeof(iweatherreader), newtypeconstructor(typeof(weatherreaderimpl), s);instance = singleton;/ <summary>/ 根据注册的目标抽象类型, 返回一个实体类型及其构造参数数组/ </summar

14、y>/ <param name=type></param>/ <returns></returns>public typeconstructor thistype typegettypeconstructor result;if (!dictionary.trygetvalue(type, out result)return null;elsereturn result;public class assembler<t>where t : class/ <summary>/ 其实 typemap 工程上本身就是个需要

15、注入的类型,可以通过访问配置系统获得,/ 这里为了示例的方便,手工配置了一些类型映射信息。/ </summary>private static itypemap map = memorytypemap.instance;public t create()typeconstructor constructor = maptypeof(t);if (constructor != null)if (constructor.constructorparameters = null)return (t)activator.createinstance(constructor.type); e

16、lsereturn (t)activator.createinstance(constructor.type, constructor.constructorparameters);elsereturn null;unit testusing microsoft.visualstudio.testtools.unittesting;using visionlogic.training.dependencyinjection.scenario; namespacevisionlogic.training.dependencyinjection.scenario.unittest testclas

17、s()public class assemblertesttestmethod public void test()iweatherreader reader = newassembler<iweatherreader>().create();assert.isnotnull(reader);assert.areequal<system.type>(typeof(weatherreaderim pl), reader.gettype();3 .经典方式下的注入实现在完成了 assembler 这个基础环境后,就是怎么注入的问题了,下面是对三种方式的经典方法实现:3.1

18、constructor injection 方式unit test - constructorusing microsoft.visualstudio.testtools.unittesting;using visionlogic.training.dependencyinjection.scenario;namespacevisionlogic.training.dependencyinjection.scenario.unittesttestclasspublic class constructorinjectiontestclass clientprivate iweatherreade

19、r reader;public client(iweatherreader reader)this.reader = reader;testmethodpublic void test()iweatherreader reader = newassembler<iweatherreader>().create();client client = new client(reader);assert.isnotnull(client);3.2 setter injection 方式unit test - setterusing microsoft.visualstudio.testto

20、ols.unittesting;using visionlogic.training.dependencyinjection.scenario; namespacevisionlogic.training.dependencyinjection.scenario.unittest testclasspublic class setterinjectiontestclass clientprivate iweatherreader reader;public iweatherreader readerget return reader; set reader = value; testmetho

21、dpublic void test()iweatherreader reader = newassembler<iweatherreader>().create();client client = new client();client.reader = reader;assert.isnotnull(client.reader);3.3 interface injection 方式unit test - interfaceusing microsoft.visualstudio.testtools.unittesting;using visionlogic.training.de

22、pendencyinjection.scenario; namespacevisionlogic.training.dependencyinjection.scenario.unittest testclasspublic class interfaceinjectiontestinterface iclientwithweatherreaderiweatherreader reader get; set;class client : iclientwithweatherreaderprivate iweatherreader reader;#region iclientwithweather

23、reader memberspublic iweatherreader readerget return reader; set reader = value; #endregiontestmethodpublic void test()iweatherreader reader = newassembler<iweatherreader>().create();client client = new client();iclientwithweatherreader clientwithreader = client;clientwithreader.reader = reade

24、r;assert.isnotnull(clientwithreader.reader);4 . 用属性( attribute )注入c#还可以通过attribute注入,enterprise library中大量使用这种方式将各种第三方机制加入到类系统中。例如:运行监控需要的 performance counter 。用于构造过程的指标信息。用于日志、密码处理。等等注: java 语言虽然发展比较慢,但在 java 5种也提供了类似的 annotation 的机制, 换了个名字省去被评估为 “抄袭”的嫌疑。 )为了演示方便,下面设计一个应用情景:scenario1 、 应用需要一个集中的机制了

25、解系统中实际创建过多少个特定类型对象的实例,用于评估系统的 capacity 要求。2 、 为了防止系统资源被用尽,需要控制每类对象实例数量。怎么实现呢?如下:增加一个内存的注册器,登记每个类已经创建过的实例实例数量。然后给每个类贴个标签 attribute ,让 assembler 在生成 的对象的时候根据标签的内容把把登记到注册器。3 .1 定义抽象业务实体c# using system;namespacevisionlogic.training.dependencyinjection.scenario.attributer/ <summary>/ 抽象的处理对象/ </

26、summary>public interface iobjectwithguidstring guid get; set;定义需要注入的限制接口,并用一个attribute 管理它c#using system;namespacevisionlogic.training.dependencyinjection.scenario.attributer / <summary>/ 需要注入的用以限制最大数量的接口/ </summary>public interface icapacityconstraintint max get;public class capacity

27、constraint : icapacityconstraintprivate int max;public capacityconstraint()this.max = 0; / 默认情况下不限制public capacityconstraint(int max) this.max = max; public int max get return max; attributeusage(attributetargets.class, allowmultiple = false) public class constraintattribute : attributeprivate icapa

28、cityconstraint capacity;public constraintattribute(int max) this.capacity = newcapacityconstraint(max); public constraintattribute() this.capacity = null; public icapacityconstraint capacity get return capacity; assembler 上增加通过attribute 注入限制的响应c#using system;using system.collections.generic;namespac

29、evisionlogic.training.dependencyinjection.scenario.attributerpublic class assembler/ <summary>/ 登记相关类型对“最大容量”属性的使用情况/ </summary>private idictionary<type, constraintattribute>attributeregistry =new dictionary<type, constraintattribute>();/ <summary>/ 登记每个类型(如须受到“最大容量”属性限

30、制的话)实际已经创建的对象数量/ </summary>private idictionary<type, int> usageregistry = newdictionary<type, int>();public t create<t>()where t : iobjectwithguid, new()icapacityconstraint constraint =getattributedefinedmax(typeof(t);if (constraint = null) | (constraint.max <= 0) / max &l

31、t;= 0 代表是不需要限制数量的。return internalcreate<t>();elseif (usageregistrytypeof(t) < constraint.max) / 检查是否超出容量限制usageregistrytypeof(t)+; / 更新使用情况注册信息return internalcreate<t>();elsereturn default(t);/ helper method/ 直接生成特定实例,并setter 方式注入其guid 。private t internalcreate<t>()where t : iob

32、jectwithguid, new()t result = new t();result.guid = guid.newguid().tostring();return result;/ helper method./ 获取特定类型所定义的最大数量, 同时视情况维护attributeregistry 和 usageregistry 的注册信息。private icapacityconstraint getattributedefinedmax(typetype)constraintattribute attribute = null;if (!attributeregistry.trygetv

33、alue(type, out attribute) / 新的待 创建的类型/ 填充相关类型的“最大容量”属性注册信息object attributes =type.getcustomattributes(typeof(constraintattribute), false);if (attributes = null) | (attributes.length <= 0) attributeregistry.add(type, null);elseattribute = (constraintattribute)attributes0;attributeregistry.add(type, attribute);usageregistry.add(type, 0); / 同时补充该类型的使用情况注册信息if (attribute = null)return null;elsereturn attribute.capacity;4 .2 对方案的测试c#using microsoft.visualstudio.testtools.unittesting;usingvisionlogic.trai

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论