昨天有网友Call我留言,说他写了个DI架构,让我有时间看看,于是我就上去看了:
文章地址: http://www.cnblogs.com/lenic/archive/2013/06/04/3117893.html
发现是E文的,于是回复:
路过秋天 22: 32: 43 怎么还写E文的 NOoK 22: 33: 03 直接在 CodePlex 上写的介绍 NOoK 22: 33: 08 然后就粘过来了 NOoK 22: 33: 15 结果还被博客园下架了。。。 NOoK 22: 34: 40 悲催的 路过秋天 22: 35: 34 谁让你 Look it on CodePlex NOoK 22: 35: 41 。。。
我错了
接着他让我下载他的处女开源项目,让我看看:
NOoK 22 : 36 : 09 明天再修改一下吧 路过秋天 22: 37: 26 平时都习惯写英文? NOoK 22: 37: 40 肯定不是啊 NOoK 22: 38: 03 在 CodePlex 上面写了,懒得改就贴过来了 路过秋天 22: 38: 43 平时也在CodePlex上面写? NOoK 22: 38: 54 第一个开源项目。。。 NOoK 22: 40: 35 改好了再说吧 路过秋天 22: 40: 46 NOoK 22: 41: 14 你可以下载代码看看,维护了好几年了 路过秋天 22: 43: 17 Ok,我看看 路过秋天 22: 48: 23 扫了一下,不是很理解 NOoK 22: 50: 15 思路是,注册委托进字典,需要的时候,再用委托生成对象 NOoK 22: 50: 41 然后衍生了多个生存周期; 路过秋天 22: 50: 50 实用场景 ? NOoK 22: 51: 42 一样啊 NOoK 22: 51: 56 就是IOC的使用场景 NOoK 22: 52: 46 这个核心代码,类似于Ninject,没配置
由于本人对IOC,虽然曾多次看过网上的文章介绍,除了“依赖注入,控制反转”这八个字,没有多余的存档记忆了。
后来的聊天,我让他给我举一个实用场景,毕竟我是实战派的,一种模式或一种东东,只有摆在现实的场景,并且有过深刻的使用痕迹,才能记的深,用的劳。
像那些设计模式的文章,一来就是UML图,举个例子基本就是猫和狗,要不就是会飞的鸭子。 费我十八层脑力,终于理解明白了。 第二天一睡醒,基本没印象。 再过些天。。。连设计模式的名字都不知叫什么了。
后来中间拧了一阵,基本没拧在一块,我要的是实战场景,他老给我讲理论。
于是我就迁就他学理论了,我搜索看了看IOC的的基本介绍:
控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题。 控制反转还有一个名字叫做依赖注入(Dependency Injection)。简称DI。 早在2004年,Martin Fowler就提出了“哪些方面的控制被反转了?”这个问题。他总结出是依赖对象的获得被反转了。基于这个结论,他为控制反转创造了一个更好的名字:依赖注入。许多非凡的应用(比HelloWorld.java更加优美,更加复杂)都是由两个或是更多的类通过彼此的合作来实现业务逻辑,这使得每个对象都需要,与其合作的对象(也就是它所依赖的对象)的引用。如果这个获取过程要靠自身实现,那么如你所见,这将导致代码高度耦合并且难以测试。 IoC 亦称为 “依赖倒置原理”( " Dependency Inversion Principle ")。差不多所有框架都使用了“倒置注入(Fowler 2004)技巧,这可说是IoC原理的一项应用。SmallTalk,C++, Java 或各种.NET 语言等面向对象程序语言的程序员已使用了这些原理。 控制反转是Spring框架的核心。 应用控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用,传递给它。也可以说,依赖被注入到对象中。所以,控制反转是,关于一个对象如何获取他所依赖的对象的引用,这个责任的反转。 oC就是IoC,不是什么技术,与GoF一样,是一种设计模式。
当然了,资料还有一大堆,就不详细贴了。
后来他为了拧过我,决定给我培训一下,给我出了道题:
NOoK 23: 30: 52 要不要做题? NOoK 23: 31: 28 一个四则运算的 路过秋天 23: 32: 06 说说看 NOoK 23: 32: 06 好吧,我睡觉了,晚安咯 NOoK 23: 33: 30 通过不同的配置,加载不同的程序集,实现四则运算 NOoK 23: 34: 19 输入两个参数,运算得到结果 NOoK 23: 35: 01 不允许用反射 路过秋天 23: 35: 31 其它条件呢? NOoK 23: 35: 39 没有了 路过秋天 23: 36: 33 使用程序集,却不允许用反射? NOoK 23: 36: 42 我就是想通过两个数字,得到结果,怎么运算我不管,只要给我结果就好 NOoK 23: 37: 07 你可以写到一个程序集里面 路过秋天 23: 37: 28 过程就是修改配置? NOoK 23: 37: 29 我说错了,加载程序集就是反射 NOoK 23: 37: 51 哥,你问偏了 NOoK 23: 38: 11 过程你写好 NOoK 23: 38: 34 通过配置修改 路过秋天 23: 38: 51 OK,我try一下 NOoK 23: 39: 09 我等你消息啊,哈哈
根据要求,2-3分钟后,我就上图了:
根据配置,加个switch,解决了,不过既然我们在说IOC,肯定是需要不断演进。
NOoK 0: 02: 58 如果这里的业务很负责呢 NOoK 0: 03: 04 不是简单的四则运算 NOoK 0: 03: 08 你怎么办 路过秋天 0: 03: 14 很负责? NOoK 0: 03: 43 很复杂 NOoK 0: 03: 48 手误 路过秋天 0: 03: 50 既然是题目,你就举个场景,我按场景实现 NOoK 0: 04: 06 你应该写4个类,对不对 NOoK 0: 04: 11 分别实现四种运算 NOoK 0: 04: 21 比这样写一个类要好 NOoK 0: 04: 28 以后扩展就可以再写一个类
好吧,很复杂的话,要分类就这样了:
IOC演进
using System; using System.Collections.Generic; using System.Text; using System.IO; namespace ABCD { class Program { static void Main( string[] args) { while ( true) { Console.Read(); int result = GetResult( 12, 4); Console.WriteLine(result); } } static int GetResult( int a, int b) { switch (GetConfig()) { case " + ": return new A().GetResult(a,b); case " - ": return new B().GetResult(a, b); case " * ": return new C().GetResult(a, b); case " / ": return new D().GetResult(a, b); } return 0; } static string GetConfig() { string config = AppDomain.CurrentDomain.BaseDirectory + " config.ini "; return File.ReadAllText(config); } } public class A { public int GetResult( int a, int b) { return a + b; } } public class B { public int GetResult( int a, int b) { return a - b; } } public class C { public int GetResult( int a, int b) { return a * b; } } public class D { public int GetResult( int a, int b) { return a / b; } }
刚发过去,就来一句:可以提取一个接口吗
改了改,代码发过去如下:
IOC 演进2
using System; using System.Collections.Generic; using System.Text; using System.IO; namespace ABCD { class Program { static void Main( string[] args) { while ( true) { Console.Read(); int result = GetResult( 12, 4); Console.WriteLine(result); } } static int GetResult( int a, int b) { IGetResult iResult = null; switch (GetConfig()) { case " + ": iResult = new A(); break; case " - ": iResult = new B(); break; case " * ": iResult = new C(); break; case " / ": iResult = new D(); break; } return iResult.GetResult(a, b); } static string GetConfig() { string config = AppDomain.CurrentDomain.BaseDirectory + " config.ini "; return File.ReadAllText(config); } } public interface IGetResult { int GetResult( int a, int b); } public class A : IGetResult { public int GetResult( int a, int b) { return a + b; } } public class B : IGetResult { public int GetResult( int a, int b) { return a - b; } } public class C : IGetResult { public int GetResult( int a, int b) { return a * b; } } public class D : IGetResult { public int GetResult( int a, int b) { return a / b; } }
四则变五则:
NOoK 0: 11: 51 我现在要加一个业务 NOoK 0: 12: 00 逻辑是 (a + b) * a NOoK 0: 12: 07 怎么办
只是加一个类和修改switch,代码过去如下:
IOC 演进3
using System; using System.Collections.Generic; using System.Text; using System.IO; namespace ABCD { class Program { static void Main( string[] args) { while ( true) { Console.Read(); int result = GetResult( 12, 4); Console.WriteLine(result); } } static int GetResult( int a, int b) { IGetResult iResult = null; switch (GetConfig()) { case " + ": iResult = new A(); break; case " - ": iResult = new B(); break; case " * ": iResult = new C(); break; case " / ": iResult = new D(); break; case " +* ": iResult = new E(); break; } return iResult.GetResult(a, b); } static string GetConfig() { string config = AppDomain.CurrentDomain.BaseDirectory + " config.ini "; return File.ReadAllText(config); } } public interface IGetResult { int GetResult( int a, int b); } public class A : IGetResult { public int GetResult( int a, int b) { return a + b; } } public class B : IGetResult { public int GetResult( int a, int b) { return a - b; } } public class C : IGetResult { public int GetResult( int a, int b) { return a * b; } } public class D : IGetResult { public int GetResult( int a, int b) { return a / b; } } public class E : IGetResult { public int GetResult( int a, int b) { return (a + b) * a; } }
最后的需求,消灭switch
NOoK 0: 15: 31 NOoK 0: 15: 37 这里能写一个字典吗 NOoK 0: 15: 46 这不就是键值对吗
OK,我给出了最终代码:
using System; using System.Collections.Generic; using System.Text; using System.IO; namespace ABCD { class Program { static void Main( string[] args) { while ( true) { Console.Read(); int result = GetResult( 12, 4); Console.WriteLine(result); } } static int GetResult( int a, int b) { IGetResult iResult = null; string key = GetConfig(); iResult = ResultRegList.ResultList[key]; return iResult.GetResult(a, b); } static string GetConfig() { string config = AppDomain.CurrentDomain.BaseDirectory + " config.ini "; return File.ReadAllText(config); } } public interface IGetResult { int GetResult( int a, int b); } public class A : IGetResult { public int GetResult( int a, int b) { return a + b; } } public class B : IGetResult { public int GetResult( int a, int b) { return a - b; } } public class C : IGetResult { public int GetResult( int a, int b) { return a * b; } } public class D : IGetResult { public int GetResult( int a, int b) { return a / b; } } public class E : IGetResult { public int GetResult( int a, int b) { return (a + b) * a; } } public class ResultRegList { static Dictionary< string, IGetResult> resultList = new Dictionary< string, IGetResult>( 5); public static Dictionary< string, IGetResult> ResultList { get { if (resultList.Count == 0) { resultList.Add( " + ", new A()); resultList.Add( " - ", new B()); resultList.Add( " * ", new C()); resultList.Add( " / ", new D()); resultList.Add( " +* ", new E()); } return resultList; } } } }
接下来就是终结理论了:
NOoK 0: 23: 45 这就是了 路过秋天 0: 24: 22 这东西写了很多,不过没感觉ioc的存在 NOoK 0: 24: 23 如果以后再添加逻辑,只要写一个IGetResult接口的实现类,再配置到字典里面,就可以了 NOoK 0: 24: 34 那个字典,其实就是ioc的雏形 NOoK 0: 24: 44 IOC说到底,就是这样的一个字典 NOoK 0: 25: 03 通过一个条件,找到所需的实现类 NOoK 0: 25: 09 然后用到逻辑上 NOoK 0: 25: 12 就好了 NOoK 0: 25: 27 再改进,就是用IOC替换你那个字典了
路过秋天 0:25:32
通过一个条件,找到所需要的实现类,看似更像反射
NOoK 0:25:40
这是反射吗?
NOoK 0:26:09
你看到了吗?
路过秋天 0:26:11
你看我的代码,如果增加一个类,ResultRegList这里还需要再注册
NOoK 0:26:18
是的
NOoK 0:26:37
字典是需要你手工改代码的
路过秋天 0:26:38
只有变更为反射,可以解决
NOoK 0:26:49
IOC就是配置一下就可以了
NOoK 0:26:54
不需要改代码了
NOoK 0:27:33
每种IOC都不一样,最简单的就是你写的这种字典
NOoK 0:27:38
然后加上反射
NOoK 0:27:42
这容易理解
NOoK 0:27:55
但是能用到生产上的IOC容器
NOoK 0:28:03
都做了很多工作的
路过秋天 0:28:15
什么工作呢?
NOoK 0:28:29
比如你可以集成AOP
NOoK 0:28:47
添加一些创建前、创建后的事件
大体上话就拧到这了,后面也没多拧几句,到了洗洗睡了的时间。
再回头看这段话,理解了,IOC原来是这样子的:
可以把IoC模式看做是工厂模式的升华,可以把IoC看作是一个大工厂,只不过这个大工厂里要生成的对象都是在XML文件中给出定义的,然后利用Java 的“反射”编程,根据XML中给出的类名生成相应的对象。从实现来看,IoC是把以前在工厂方法里写死的对象生成代码,改变为由XML文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性。
接下来理解“依赖注入、控制反转”八个字:
依赖注入:
首先Program里的switch本来是调用new A()这个对象,经过演进后,变成调用IGetResult接口了。 所以Program本来是依赖new A()对象,结果被IGetResult接口给插了菊花。 最终就是Program只调用IGetResult接口,Programe和new A()没有直接依赖关系。 这就是依赖注入的解释了。
控制反转:
正常说的, new A()对象,是由Program写死在代码里的了,相当于控制权是在Program里。 代码演进之后,对象的产生的控制权转移到配置文件里。 这就是控制(权)反转的解释了。
文章有点长,不知道你理解了没。