遵循接口分离原则是编写健壮可扩展程序的基本要求。软件的复杂性使用不断增加新层的方式解决,而层之间的耦合则依赖接口隔离的方式解决。
工厂模式是接口子类化的经典模式,工厂模式的实现方式,以前的随笔中已经详述过,故略去。这种子类化是一种主动式的子类化方法,调用者主动在需要的时候,进行子类化。而依赖注入模式则是一种被动式的子类化,其接口的子类化由调动者的调用者完成,如名字所述,其子类化的方式依靠反射功能实现注入,具体来分,又分为初始注入和属性赋值注入两种方式。在两种注入模式中,初始注入模式仅调用一次反射完成注入。
下面是一个简单的依赖注入框架:
1.子类化接口与初始注入接口类:
1 public interface IDependency { 2 Object GetService(Type type); 3 } 4 5 public class DefaultDependency : IDependency { 6 public object GetService(Type type) { 7 if (type.GetConstructor(Type.EmptyTypes) != null) { 8 return Activator.CreateInstance(type); 9 } 10 11 try { 12 //just use the first constructor 13 var cons = type.GetConstructors()[0]; 14 var paras = cons.GetParameters(); 15 var objs = new Object[paras.Length]; 16 for (int i = 0; i < paras.Length; i++) { 17 objs[i] = InstanceMap.CreateInstance(paras[i].ParameterType); 18 } 19 return cons.Invoke(objs); 20 } catch(Exception ex){ 21 throw new InstanceInitException(ex); 22 } 23 } 24 }
2.初始参数仓库
1 public static class InstanceMap { 2 public static Object CreateInstance(Type type) { 3 foreach (var unit in units) { 4 if (unit.InterfaceType == type) { 5 return unit.CreateInstance(); 6 } 7 } 8 return null; 9 } 10 11 public static void Config(Action<InstanceSetting> customConfig) { 12 customConfig(innerSetting); 13 } 14 15 public static InstanceUnit For<InterfaceType>() { 16 return For(typeof(InterfaceType)); 17 } 18 19 public static InstanceUnit For(Type interfaceType) { 20 foreach (var un in units) { 21 if (un.InterfaceType == interfaceType) return un; 22 } 23 24 var unit = new InstanceUnit(); 25 unit.InterfaceType = interfaceType; 26 27 units.AddLast(unit); 28 return unit; 29 } 30 31 static LinkedList<InstanceUnit> units = new LinkedList<InstanceUnit>(); 32 static InstanceSetting innerSetting = new InstanceSetting(); 33 } 34 35 public class InstanceUnit{ 36 internal InstanceUnit() { 37 useSingleMode = false; 38 } 39 40 public Object CreateInstance() { 41 if (useSingleMode) { 42 if (singleInstance == null) { 43 if (instanceType == null) return null; 44 singleInstance = createInstance(instanceType, parameters); 45 } 46 return singleInstance; 47 } else { 48 if (instanceType == null) return null; 49 else return createInstance(instanceType,parameters); 50 } 51 } 52 53 public Type InterfaceType { get; set; } 54 55 Object[] parameters { get; set; } 56 Type instanceType { get; set; } 57 Boolean useSingleMode { get; set; } 58 Object singleInstance { get; set; } 59 60 public InstanceUnit Reflect<InstanceType>() { 61 return Reflect(typeof(InstanceType)); 62 } 63 public InstanceUnit Reflect(Type type) { 64 instanceType = type; 65 return this; 66 } 67 68 public InstanceUnit ReflectSingle(Object singleInstance) { 69 useSingleMode = true; 70 singleInstance = singleInstance; 71 return this; 72 } 73 public InstanceUnit ReflectSingle<InstanceType>() { 74 useSingleMode = true; 75 this.instanceType = typeof(InstanceType); 76 return this; 77 } 78 79 public InstanceUnit Paras(params Object[] objs) { 80 parameters = objs; 81 return this; 82 } 83 84 static Object createInstance(Type type, Object[] paras) { 85 var paraTypes = new Type[paras.Length]; 86 87 var i = 0; 88 Array.ForEach(paras, value => { 89 paraTypes[i] = value.GetType(); 90 i++; 91 }); 92 93 var constructor = type.GetConstructor(paraTypes); 94 try { 95 return constructor.Invoke(paras); 96 } catch { 97 return null; 98 } 99 } 100 } 101 102 //for test 103 public class InstanceSetting { 104 public void AutoScanRegistedInstance() { } 105 public void AllowScanAllAssemblies(){} 106 }
3.使用方式
1 class Program { 2 static void Main(string[] args) { 3 4 InstanceMap.Config(cfg => { 5 cfg.AllowScanAllAssemblies(); 6 }); 7 InstanceMap.For<ITemp>().ReflectSingle<Temp>().Paras("wangjieas"); 8 ((new DefaultDependency()).GetService(typeof(TempUser)) as TempUser).Say(); 9 10 Console.Read(); 11 } 12 } 13 14 public interface IMessage { 15 String Message { get; } 16 } 17 18 public class Message : IMessage { 19 public Message(String str) { 20 innerString = str; 21 } 22 23 string IMessage.Message { 24 get { return innerString; } 25 } 26 27 String innerString; 28 } 29 30 public interface ITemp { 31 String Message { get; } 32 } 33 34 public class Temp : ITemp { 35 public Temp(String name) { 36 this.name = name; 37 } 38 39 public string Message { 40 get { return String.Format("Hello,{0}", name); } 41 } 42 43 String name; 44 } 45 46 47 public class TempUser { 48 public TempUser(ITemp temp) { 49 this.temp = temp; 50 } 51 52 public void Say() { 53 Console.WriteLine(temp.Message); 54 } 55 56 ITemp temp; 57 }
后记1:上面是一个简单的实现,仅仅用于测试。有兴趣的话,可以继续扩展。比如InstanceUnit中的Para,可以单独出来,分别对应配置文件项目和类attr值。DI经典的使用方式,自动化测试与mvc模式。
后记2:DI是spring框架的拿手好戏,最新的asp.net mvc框架也加入了这一层,它在带来简便性的同时也引入了复杂性,两层反射模式的使用必定会对性能带来损耗。所以,请时刻秉持简单易用的原则,谨慎的选择合适的方式来组织代码。