博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
傻嘎,IOC原来是这样子的
阅读量:6251 次
发布时间:2019-06-22

本文共 8844 字,大约阅读时间需要 29 分钟。

昨天有网友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 
以后扩展就可以再写一个类

 

好吧,很复杂的话,要分类就这样了:

 

ExpandedBlockStart.gif
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;
        }
    }

 

 

刚发过去,就来一句:可以提取一个接口吗

改了改,代码发过去如下:

ExpandedBlockStart.gif
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,代码过去如下:

ExpandedBlockStart.gif
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里。
代码演进之后,对象的产生的控制权转移到配置文件里。
这就是控制(权)反转的解释了。

 

 

文章有点长,不知道你理解了没。

你可能感兴趣的文章
写了一个bug----使用已经被删除的内存
查看>>
清空表且自增的id重新从0开始
查看>>
[杂记]如何在LaTeX里插入高亮代码
查看>>
解决数据架构难点数据分布的六种策略
查看>>
mysql 存储过程创建
查看>>
centos7 composer安装
查看>>
「常微分方程」(阿諾爾德) Page 6 問題4 經過擴張相空間的每一點有且僅有一條積分曲線...
查看>>
同一个闭区间上有界变差函数的和与积都是有界变差函数
查看>>
java安全证书配置
查看>>
uikit学习
查看>>
使用erlang 建立一个自动化的灌溉系统(1)准备工作
查看>>
python 调用aiohttp
查看>>
LPAD、RPAD补位函数
查看>>
mysql 案例~ mysql故障恢复
查看>>
UESTC 1307 windy数(数位DP)
查看>>
关于JS面向对象、设计模式、以及继承的问题总结
查看>>
Spring Boot中使用MyBatis注解配置详解
查看>>
MatLab实现FFT与功率谱
查看>>
答《漫话ID》中的疑问:UniqueID和ClientID的来源
查看>>
STL容器--学习笔记
查看>>