约定优于配置是一个简单的概念。 系统,类库,框架应该假定合理的默认值,而非要求提供不必要的配置。 流行的框架如 Ruby on Rails 和 EJB3 已经开始坚持这些原则,以对像原始的 EJB 2.1 规范那样的框架的配置复杂度做出反应。 一个约定优于配置的例子就像 EJB3 持久化,将一个 特殊的Bean持久化,你所需要做的只是将这个类标注为 @Entity 。 框架将会假定表名和列名是基于类名和属性名。 系统也提供了一些钩子,当有需要的时候你可以重写这些名字,但是,在大部分情况下,你会发现使用框架提供的默认值会让你的项目运行的更快。
上午闲暇的时候,开发了一个简单的基于Coc策略的对象工厂。目的是验证一下Coc的基本实现逻辑.
因为Coc是一种比较好的简单化DI实现、配置和维护的好方式,虽然它代替不了XML来说明依赖关系,因为对于值类型注入
和接口注入它还无能为力,但是为大大简化DI的实现、配置以及维护提供很好的改进,实际中应该将Coc和XML声明的方式
结合起来使用。下面是我写的一个简单的实现,不过后期将不断改善和丰富。
using System; using System.Collections; using System.Collections.Generic; using System.Reflection; namespace DotNetConsole { public delegate void InitObjectRelationShip (object thisObj); public class ObjectFactory { private static Random NextObj = null; public static object Create (Type ObjectType, InitObjectRelationShip InitMethod) { object CachedObject = CreateObject (ObjectType, InitMethod); if (NextObj == null) NextObj = new Random (); if (ObjectPoolService.GetCahce ().ContainsKey (ObjectType.ToString ())) { ObjectPoolService.SetCurrentPoolInUse (true); if (ObjectPoolService.GetCahce ()[ObjectType.ToString ()].Count < ObjectPoolService.GetMaxSize ()) { ObjectPoolService.GetCahce ()[ObjectType.ToString ()].Add (CachedObject); } int index = (int)NextObj.Next (ObjectPoolService.GetCahce ()[ObjectType.ToString ()].Count); return ObjectPoolService.GetCahce ()[ObjectType.ToString ()][index]; } else { if (ObjectPoolService.GetMaxSize () > ZERO_VALUE) { ObjectPoolService.SetCurrentPoolInUse (true); ObjectPoolService.GetCahce ().Add (ObjectType.ToString (), new List<object> ()); ObjectPoolService.GetCahce ()[ObjectType.ToString ()].Add (CachedObject); } } ObjectPoolService.SetCurrentPoolInUse (false); return CachedObject; } ///in the future ,will implement proxy object not real object and ///base on proxy way, for object's method calling will be done as parall way. private static object CreateObject (Type ObjectType, InitObjectRelationShip InitMethodDef) { object FirstHeadObject = Activator.CreateInstance (ObjectType); if (InitMethodDef == null) { Type[] interfaceTypes = ObjectType.GetInterfaces (); if (interfaceTypes.Length != 0) { foreach (Type interfaceSubType in interfaceTypes) { if (interfaceSubType.Equals (typeof(IUserModeObjectInitor))) { ((IUserModeObjectInitor)FirstHeadObject).Init (); } } } PropertyInfo[] proInfos = ObjectType.GetProperties (); if (proInfos.Length != 0) { foreach (PropertyInfo pro in proInfos) { if (pro.GetValue (FirstHeadObject, null) == null) { SetDepthObjectRelationShip (FirstHeadObject, pro); } } } } else { InitMethodDef (FirstHeadObject); } return FirstHeadObject; } private static void SetDepthObjectRelationShip (object MainObject, PropertyInfo PropertyType) { object PropertyObject = Activator.CreateInstance (PropertyType.PropertyType); PropertyType.GetSetMethod ().Invoke (MainObject, new object[] { PropertyObject }); PropertyInfo[] innerProps = PropertyType.PropertyType.GetProperties (); if (innerProps.Length != 0) { foreach (PropertyInfo innerProerty in innerProps) { SetDepthObjectRelationShip (PropertyObject, innerProerty); } } } private const int ZERO_VALUE = 0; } }
在ObjectFactory中,还使用了一个基于无状态对象的ObjectPoolService(实现在下面,暂时提供最简单的实现),目的是提供对象重用率减少内存占用有量,并且提供随机的对象获取机制,因为客户程序不关心同一类型的多个对象哪一个为其提供服务。并且在MaxSize=0时,可以不使用ObjectPoolService而直接Create Object. MaxSize越大,Pool中某一个对象的重用机会就越小,并且内存占用率也比较高,所以MaxSize可以用来微调内存占有率和重用几率的配置。
值得注意的是Create方法中提供一个类型为
InitObjectRelationShip
的委托参数,它可以由调用者指定对象关系组装的方式(如果不想使用Coc的话,或者结合XML配置和Coc的话,可以看一下下面的测试代码),组合各种DI策略就比较灵活了.
另外还设计了一个叫做IUserModeObjectInitor的接口,用于跳过组装过程,开发者自己组装对象关系,但是侵入性比较强,
建议使用ObjectFactory的Create方法中的委托参数干这件事情,这里保留这个接口的目的是为了方式灵活,不过以方法的委托参数的处理为优先!
using System; namespace DotNetConsole { public interface IUserModeObjectInitor { void Init(); } }
为了提高性能,还设计了一个极为简单的Cache:
using System; using System.Collections.Generic; namespace DotNetConsole { //use cassanadra db repleace in the future. public class ObjectPoolService { private static volatile Dictionary<string,List<object>> m_ObjectCache=null; private static int m_MaxSize=0; private static bool m_IsInUse=false; public static Dictionary<string, List<object>> GetCahce () { if (m_ObjectCache == null) m_ObjectCache = new Dictionary<string, List<object>> (); return m_ObjectCache; } public static void SetMaxSize (int Size) { m_MaxSize = Size; } public static int GetMaxSize () { return m_MaxSize; } public static void SetCurrentPoolInUse (bool isInUse) { m_IsInUse = isInUse; } public static bool GetCurrentPoolUsingState () { return m_IsInUse; } public static void ShutdownPool () { if (!m_IsInUse) { m_ObjectCache.Clear (); } } } }
测试的代码:
using System; using System.Reflection; namespace DotNetConsole { class MainClass { //statefulless Object gen! can reduce mem. public static void Main (string[] args) { ObjectPoolService.SetMaxSize (100); double timestart = DateTime.Now.Millisecond; ((OrderService)ObjectFactory.Create (typeof(OrderService), x => ((OrderService)x).DAOObject = (OrderDAO)ObjectFactory.Create (typeof(OrderDAO), null))).ShowInfo (); for (int i = 0; i < 1000; i++) { ((OrderService)ObjectFactory.Create (typeof(OrderService), null)).ShowInfo (); } ObjectPoolService.SetCurrentPoolInUse (false); ObjectPoolService.ShutdownPool (); double timeend= DateTime.Now.Millisecond; Console.WriteLine (timeend-timestart); Console.ReadLine(); } } }
以上,基本思路就是先排除用XML来定义类之间的依赖关系,当前先暂时开发一个最基本的只有基于Coc策略的基本对象工厂,实现方式很简单,就是利用反射获取目标类型的属性列表,然后取得属性的类型(暂时只是引用类型),然后还是利用反射实例化对应属性类型的对象并注入到目标对象中,然后递归地这样处理,所有依赖的对象都初始化并组装进来。
当然这是一个最基本的实现。基于这个在考虑拓展,是比较好的方式,逐步演化比较好。后续还有很多工作要做,
处理这个简单的工厂,必须以此为基础开发达到能使用级别的分布式对象Cache、分布式的层次型的Object容器、基于代理的对象创建、AOP、由于要采用Emit来创建代理对象的方式来实现基于代理的类型创建,然后将此”虚拟“出来的类型传给这个ObjectFactory来生成对象,那么有可能在“虚拟”出来的类型(代理)中插入任何想干的事情,比如前面的AOP或者只有在调用一个方法时在实例化目标“能干活”的真实对象(延迟加载),另外能够将代理类用Emit伪装成WCF的服务类型(插入服务契约等Attribute等),那么就可以实现对象服务的发出(向网络公布成服务)的功能,以上这些增强,我会逐步加入,都是以当前这个ObjectFactory作为最底层的基础的。后续的改进代码会陆续登录上来的