依赖注入(一)工厂
许多时候我想等我把某些问题,完全想明白,完全理解透,再写点东西;事实往往相反,发现等我真正接近,把某些东西理解好,我自己再也不屑写点东西分享了,没有精神气也好,不着调也好,这对于我的性格至少是一个事实。懂得越多的人或理解的越深的时候,人越容易沉默;半调子的时候我一直在叫嚣,就像现在的我。
决定改变方式,不要等想好了再动笔,也不要等完全弄明白了再动笔,看点写点。
小马过河,亲自走一走就会知道。
“不要信我,否则你死得很惨!”
做事,不能只想。现在的我喜欢做,只需要想个清晰的目标就可以。
人想进步 就是不满足于现状,想把好东西投入生产就需要一点一滴的积累,做好技术的沉淀吧,这样面对高一点的层次时,也能游刃有余。
跑题了。下面开始举个栗子。在网上抄的,但很能说明问题。这里就让这段话来引入吧。
现实世界中,会有这么一种情况。
比如,汽车不会强依赖于某个品牌的轮胎,任何公司生产的轮胎,只要符合汽车的接口,就可以装在这个汽车上使用。
还有电脑的USB接口,只要符合USB标准的外设,就都能够接上电脑使用。
面向对象编程也是如此。
那到底什么是依赖注入呢?依赖注入(Dependency Injection)。简称DI,它还有另外一个名字,控制反转(Inversion of Control,英文缩写为IoC)。
当某个角色(可能是一个实例,调用者)需要另一个角色(另一个实例,被调用者)的协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在依赖注入里,创建被调用者的工作不再由调用者来完成,因此称为控制反转;创建被调用者实例的工作通常由依赖注入容器来完成,然后注入调用者,因此也称为依赖注入。
用了些专业术语,有点高端大气上档次的感觉,我觉得其实就是抽象工厂,类似数据库抽象工厂DataAccess类(抽象工厂模式创建DAL)。
下面给出DataAccess类实现。需要配置web.config和加一个Cache包装类。
1 public sealed class DataAccess<T> 3 { 5 private static readonly string path = System.Configuration.ConfigurationManager.AppSettings["WebDAL"]; 7 /// <summary> 9 /// 创建对象或从缓存获取 11 /// </summary> 13 public static object CreateObject(string path, string CacheKey) 15 { 17 object objType = DataCache.GetCache(CacheKey);//从缓存读取 19 if (objType == null) {
try 25 { 27 objType = Assembly.Load(path).CreateInstance(CacheKey);//反射创建 29 DataCache.SetCache(CacheKey, objType);// 写入缓存 31 } 33 catch 35 { } 37 } 39 return objType; 41 } 42 43 /// <summary> 44 45 /// 根据配置文件生成一个传入的对象,并将其转为IDAL.IDAL泛型接口 46 47 /// </summary> 48 49 public static IDAL<T> GetIDAL(String Obj) 50 51 { 52 53 string CacheKey = path + "." + Obj; 54 55 object objType = CreateObject(path, CacheKey); 56 57 return (IDAL<T>)objType; 58 59 } 60 61 /// <summary> 62 63 /// 创建非IDAL接口的数据层接口 64 65 /// </summary> 66 67 public static T CreateDAL(String Obj) 68 69 { 70 71 string CacheKey = path + "." + Obj; 72 73 object objType = CreateObject(path, CacheKey); 74 75 return (T)objType; 76 77 } 78 79 }
上面又有点跑题的感觉。下面给出一个简单例子对比一下,体现出控制反转。
ChineseMan 有2个方法
1 public class ChineseMan 3 { 5 public void Say() 7 { 9 Console.WriteLine("中文"); 11 } 13 public void Sex() 15 { 17 Console.WriteLine("男"); 19 } 23 }
使用
class Program { static void Main(string[] args) { ChineseMan cm =new ChineseMan();// 改变这里 cm.Say(); cm.Sex(); } }
当加入另一个人时就会出现
EnglishMan
1 public class EnglishMan 3 { 5 public void Say() 7 { 9 Console.WriteLine("English"); 11 } 13 public void Sex() 15 { 17 Console.WriteLine("male"); 19 } 20 }
class Program { static void Main(string[] args) { EnglishMan em=new EnglishMan();// 改变这里 em.Say(); em.Sex(); } }
使用多态
//接口
public interface IPeople { void Say(); void Sex(); }
//继承接口 public class ChineseMan : IPeople { public void Say() { Console.WriteLine("中文"); } public void Sex() { Console.WriteLine("男"); } } public class EnglishMan : IPeople { public void Say() { Console.WriteLine("English"); } public void Sex() { Console.WriteLine("male"); } } static void Main(string[] args) {
//多态 IPeople pe = new ChineseMan(); pe.Say(); pe.Sex(); pe = new EnglishMan(); pe.Say(); pe.Sex(); }
使用工厂。
类还是上面多态中的。
工厂类
public class Factory { public static IPeople GetPeople() { return new ChineseMan();//需要ChineseMan new ChineseMan() 需要EnglishMan new English() } } static void Main(string[] args) { IPeople pe = Factory.GetPeople(); pe.Say(); pe.Sex(); }
有人说,这不是还得在 Factory类中换需要new的具体类? 是的 但是在变化时Main方法却不需要重新编译,当有类似ChineseMan 类新加时只需要加类比如 ChineseWoman 类,只需加类并且 只改变Factory 类,Factory类做的再好点,就加反射。
public static IPeople GetPeople() { object obj = Assembly.Load("IoCDemo").CreateInstance("IoCDemo.EnglishMan");//IoCDemo是namespace namespace IoCDemo return obj as IPeople;//需要ChineseMan new ChineseMan() 需要EnglishMan new English() }
IoCDemo IoCDemo.EnglishMan 这两个字符串可通过XML获得 在外部进行配置。这样就完全不需要改,只需要加类就行了。
下一篇介绍第三方依赖注入容器。
解除依赖不仅让代码结构看起来更加合理,其带来的另一个好处是,各个部分可以单独的做单元测试,使得单元测试能够更加容易的进行。这个对于一些复杂度高的项目,对于保证项目的稳定性和可用性非常有意义。