依赖注入那些事儿.docx
- 文档编号:4037000
- 上传时间:2023-05-06
- 格式:DOCX
- 页数:47
- 大小:318.62KB
依赖注入那些事儿.docx
《依赖注入那些事儿.docx》由会员分享,可在线阅读,更多相关《依赖注入那些事儿.docx(47页珍藏版)》请在冰点文库上搜索。
依赖注入那些事儿
写在前面的话
昨天在博客园上看到一篇博文,名为“什么是依赖注入?
(来听一个笑话)”[点击这里访问]。
可以看出,作者是想以一种轻松幽默的方式对依赖注入的思想进行一个阐述。
不过在读完文章后,个人觉得文章过于浅薄,没有真正将依赖注入的思想解释清楚,也没有深入的分析和具体的示例,所用比喻也有点低俗(至少笔者读完文章后觉得胃里不是很舒服)。
但是,这篇博文启发了我。
回想自己平常的学习和工作,依赖注入确实是软件架构和开发中一个十分重要的概念和技术,笔者参与的几乎每一个项目都用到了它,并且得到了很好的效果。
于是,笔者萌发了写这篇文章的想法,意在将依赖注入这项概念本身结合笔者的实践经验及所感所想,做一个相对完整的综述,并总结于此文。
一来可以和朋友们分享这项技术及个人心得,二来也是自己对这项技术做的一个总结。
目录
写在前面的话
目录
1IGame游戏公司的故事
1.1讨论会
1.2实习生小李的实现方法
1.3架构师的建议
1.4小李的小结
2探究依赖注入
2.1故事的启迪
2.2正式定义依赖注入
3依赖注入那些事儿
3.1依赖注入的类别
3.1.1Setter注入
3.1.2Construtor注入
3.1.3依赖获取
3.2反射与依赖注入
3.3多态的活性与依赖注入
3.3.1多态性的活性
3.3.2不同活性多态性依赖注入的选择
4IoCContainer
4.1IoCContainer出现的必然性
4.2IoCContainer的分类
4.2.1重量级IoCContainer
4.2.2轻量级IoCContainer
4.3.NET平台上典型IoCContainer推介
4.3.1Spring.NET
4.3.2Unity
参考文献
1IGame游戏公司的故事
1.1讨论会
话说有一个叫IGame的游戏公司,正在开发一款ARPG游戏(动作&角色扮演类游戏,如魔兽世界、梦幻西游这一类的游戏)。
一般这类游戏都有一个基本的功能,就是打怪(玩家攻击怪物,借此获得经验、虚拟货币和虚拟装备),并且根据玩家角色所装备的武器不同,攻击效果也不同。
这天,IGame公司的开发小组正在开会对打怪功能中的某一个功能点如何实现进行讨论,他们面前的大屏幕上是这样一份需求描述的ppt:
图1.1需求描述ppt
各个开发人员,面对这份需求,展开了热烈的讨论,下面我们看看讨论会上都发生了什么。
1.2实习生小李的实现方式
在经过一番讨论后,项目组长Peter觉得有必要整理一下各方的意见,他首先询问小李的看法。
小李是某学校计算机系大三学生,对游戏开发特别感兴趣,目前是IGame公司的一名实习生。
经过短暂的思考,小李阐述了自己的意见:
“我认为,这个需求可以这么实现。
HP当然是怪物的一个属性成员,而武器是角色的一个属性成员,类型可以使字符串,用于描述目前角色所装备的武器。
角色类有一个攻击方法,以被攻击怪物为参数,当实施一次攻击时,攻击方法被调用,而这个方法首先判断当前角色装备了什么武器,然后据此对被攻击怪物的HP进行操作,以产生不同效果。
”
而在阐述完后,小李也飞快的在自己的电脑上写了一个Demo,来演示他的想法,Demo代码如下。
Code:
怪物
1:
usingSystem;
2:
usingSystem.Collections.Generic;
3:
usingSystem.Linq;
4:
usingSystem.Text;
5:
6:
namespaceIGameLi
7:
{
8:
///
9:
///怪物
10:
///
11:
internalsealedclassMonster
12:
{
13:
///
14:
///怪物的名字
15:
///
16:
publicStringName{get;set;}
17:
18:
///
19:
///怪物的生命值
20:
///
21:
publicInt32HP{get;set;}
22:
23:
publicMonster(Stringname,Int32hp)
24:
{
25:
this.Name=name;
26:
this.HP=hp;
27:
}
28:
}
29:
}
Code:
角色
1:
usingSystem;
2:
usingSystem.Collections.Generic;
3:
usingSystem.Linq;
4:
usingSystem.Text;
5:
6:
namespaceIGameLi
7:
{
8:
///
9:
///角色
10:
///
11:
internalsealedclassRole
12:
{
13:
privateRandom_random=newRandom();
14:
15:
///
16:
///表示角色目前所持武器的字符串
17:
///
18:
publicStringWeaponTag{get;set;}
19:
20:
///
21:
///攻击怪物
22:
///
23:
///
24:
publicvoidAttack(Monstermonster)
25:
{
26:
if(monster.HP<=0)
27:
{
28:
Console.WriteLine("此怪物已死");
29:
return;
30:
}
31:
32:
if("WoodSword"==this.WeaponTag)
33:
{
34:
monster.HP-=20;
35:
if(monster.HP<=0)
36:
{
37:
Console.WriteLine("攻击成功!
怪物"+monster.Name+"已死亡");
38:
}
39:
else
40:
{
41:
Console.WriteLine("攻击成功!
怪物"+monster.Name+"损失20HP");
42:
}
43:
}
44:
elseif("IronSword"==this.WeaponTag)
45:
{
46:
monster.HP-=50;
47:
if(monster.HP<=0)
48:
{
49:
Console.WriteLine("攻击成功!
怪物"+monster.Name+"已死亡");
50:
}
51:
else
52:
{
53:
Console.WriteLine("攻击成功!
怪物"+monster.Name+"损失50HP");
54:
}
55:
}
56:
elseif("MagicSword"==this.WeaponTag)
57:
{
58:
Int32loss=(_random.NextDouble()<0.5)?
100:
200;
59:
monster.HP-=loss;
60:
if(200==loss)
61:
{
62:
Console.WriteLine("出现暴击!
!
!
");
63:
}
64:
65:
if(monster.HP<=0)
66:
{
67:
Console.WriteLine("攻击成功!
怪物"+monster.Name+"已死亡");
68:
}
69:
else
70:
{
71:
Console.WriteLine("攻击成功!
怪物"+monster.Name+"损失"+loss+"HP");
72:
}
73:
}
74:
else
75:
{
76:
Console.WriteLine("角色手里没有武器,无法攻击!
");
77:
}
78:
}
79:
}
80:
}
Code:
测试代码
1:
usingSystem;
2:
usingSystem.Collections.Generic;
3:
usingSystem.Linq;
4:
usingSystem.Text;
5:
6:
namespaceIGameLi
7:
{
8:
classProgram
9:
{
10:
staticvoidMain(string[]args)
11:
{
12:
//生成怪物
13:
Monstermonster1=newMonster("小怪A",50);
14:
Monstermonster2=newMonster("小怪B",50);
15:
Monstermonster3=newMonster("关主",200);
16:
Monstermonster4=newMonster("最终Boss",1000);
17:
18:
//生成角色
19:
Rolerole=newRole();
20:
21:
//木剑攻击
22:
role.WeaponTag="WoodSword";
23:
role.Attack(monster1);
24:
25:
//铁剑攻击
26:
role.WeaponTag="IronSword";
27:
role.Attack(monster2);
28:
role.Attack(monster3);
29:
30:
//魔剑攻击
31:
role.WeaponTag="MagicSword";
32:
role.Attack(monster3);
33:
role.Attack(monster4);
34:
role.Attack(monster4);
35:
role.Attack(monster4);
36:
role.Attack(monster4);
37:
role.Attack(monster4);
38:
39:
Console.ReadLine();
40:
}
41:
}
42:
}
程序运行结果如下:
图1.2小李程序的运行结果
1.3架构师的建议
小李阐述完自己的想法并演示了Demo后,项目组长Peter首先肯定了小李的思考能力、编程能力以及初步的面向对象分析与设计的思想,并承认小李的程序正确完成了需求中的功能。
但同时,Peter也指出小李的设计存在一些问题,他请小于讲一下自己的看法。
小于是一名有五年软件架构经验的架构师,对软件架构、设计模式和面向对象思想有较深入的认识。
他向Peter点了点头,发表了自己的看法:
“小李的思考能力是不错的,有着基本的面向对象分析设计能力,并且程序正确完成了所需要的功能。
不过,这里我想从架构角度,简要说一下我认为这个设计中存在的问题。
首先,小李设计的Role类的Attack方法很长,并且方法中有一个冗长的if…else结构,且每个分支的代码的业务逻辑很相似,只是很少的地方不同。
再者,我认为这个设计比较大的一个问题是,违反了OCP原则。
在这个设计中,如果以后我们增加一个新的武器,如倚天剑,每次攻击损失500HP,那么,我们就要打开Role,修改Attack方法。
而我们的代码应该是对修改关闭的,当有新武器加入的时候,应该使用扩展完成,避免修改已有代码。
一般来说,当一个方法里面出现冗长的if…else或switch…case结构,且每个分支代码业务相似时,往往预示这里应该引入多态性来解决问题。
而这里,如果把不同武器攻击看成一个策略,那么引入策略模式(StrategyPattern)是明智的选择。
最后说一个小的问题,被攻击后,减HP、死亡判断等都是怪物的职责,这里放在Role中有些不当。
”
Tip:
OCP原则,即开放关闭原则,指设计应该对扩展开放,对修改关闭。
Tip:
策略模式,英文名StrategyPattern,指定义算法族,分别封装起来,让他们之间可以相互替换,此模式使得算法的变化独立于客户。
小于边说,边画了一幅UML类图,用于直观表示他的思想。
图1.3小于的设计
Peter让小李按照小于的设计重构Demo,小李看了看小于的设计图,很快完成。
相关代码如下:
Code:
IAttackStrategy接口
1:
usingSystem;
2:
usingSystem.Collections.Generic;
3:
usingSystem.Linq;
4:
usingSystem.Text;
5:
6:
namespaceIGameLiAdv
7:
{
8:
internalinterfaceIAttackStrategy
9:
{
10:
voidAttackTarget(Monstermonster);
11:
}
12:
}
Code:
木剑
1:
usingSystem;
2:
usingSystem.Collections.Generic;
3:
usingSystem.Linq;
4:
usingSystem.Text;
5:
6:
namespaceIGameLiAdv
7:
{
8:
internalsealedclassWoodSword:
IAttackStrategy
9:
{
10:
publicvoidAttackTarget(Monstermonster)
11:
{
12:
monster.Notify(20);
13:
}
14:
}
15:
}
Code:
铁剑
1:
usingSystem;
2:
usingSystem.Collections.Generic;
3:
usingSystem.Linq;
4:
usingSystem.Text;
5:
6:
namespaceIGameLiAdv
7:
{
8:
internalsealedclassIronSword:
IAttackStrategy
9:
{
10:
publicvoidAttackTarget(Monstermonster)
11:
{
12:
monster.Notify(50);
13:
}
14:
}
15:
}
Code:
魔剑
1:
usingSystem;
2:
usingSystem.Collections.Generic;
3:
usingSystem.Linq;
4:
usingSystem.Text;
5:
6:
namespaceIGameLiAdv
7:
{
8:
internalsealedclassMagicSword:
IAttackStrategy
9:
{
10:
privateRandom_random=newRandom();
11:
12:
publicvoidAttackTarget(Monstermonster)
13:
{
14:
Int32loss=(_random.NextDouble()<0.5)?
100:
200;
15:
if(200==loss)
16:
{
17:
Console.WriteLine("出现暴击!
!
!
");
18:
}
19:
monster.Notify(loss);
20:
}
21:
}
22:
}
Code:
怪物
1:
usingSystem;
2:
usingSystem.Collections.Generic;
3:
usingSystem.Linq;
4:
usingSystem.Text;
5:
6:
namespaceIGameLiAdv
7:
{
8:
///
9:
///怪物
10:
///
11:
internalsealedclassMonster
12:
{
13:
///
14:
///怪物的名字
15:
///
16:
publicStringName{get;set;}
17:
18:
///
19:
///怪物的生命值
20:
///
21:
privateInt32HP{get;set;}
22:
23:
publicMonster(Stringname,Int32hp)
24:
{
25:
this.Name=name;
26:
this.HP=hp;
27:
}
28:
29:
///
30:
///怪物被攻击时,被调用的方法,用来处理被攻击后的状态更改
31:
///
32:
///
33:
publicvoidNotify(Int32loss)
34:
{
35:
if(this.HP<=0)
36:
{
37:
Console.WriteLine("此怪物已死");
38:
return;
39:
}
40:
41:
this.HP-=loss;
42:
if(this.HP<=0)
43:
{
44:
Console.WriteLine("怪物"+this.Name+"被打死");
45:
}
46:
else
47:
{
48:
Console.WriteLine("怪物"+this.Name+"损失"+loss+"HP");
49:
}
50:
}
51:
}
52:
}
Code:
角色
1:
usingSystem;
2:
usingSystem.Collections.Generic;
3:
usingSystem.Linq;
4:
usingSystem.Text;
5:
6:
namespaceIGameLiAdv
7:
{
8:
///
9:
///角色
10:
///
11:
internalsealedclassRole
12:
{
13:
///
14:
///表示角色目前所持武器
15:
///
16:
publicIAttackStrategyWeapon{get;set;}
17:
18:
///
19:
///攻击怪物
20:
///
21:
///
22:
publicvoidAttack(Monstermonster)
23:
{
24:
this.Weapon.AttackTarget(monster);
25:
}
26:
}
27:
}
Code:
测试代码
1:
usingSystem;
2:
usingSystem.Collections.Generic;
3:
usingSystem.Linq;
4:
usingSystem.Text;
5:
6:
namespaceIGameLiAdv
7:
{
8:
classProgram
9:
{
10:
staticvoidMain(string[]args)
11:
{
12:
//生成怪物
13:
Monstermonster1=newMonster("小怪A",50);
14:
Monstermonster2=newMonster("小怪B",50);
15:
Monstermonster3=newMonster("关主",200);
16:
Monstermonster4=newMonster("最终Boss",1000);
17:
18:
//生成角色
19:
Rolerole=newRole();
20:
21:
//木剑攻击
22:
role.Weapon=newWoodSword();
23:
role.Attack(monster1);
24:
25:
//铁剑攻击
26:
role.Weapon=newIronSword();
27:
role.Attack(monster2);
28:
role.Attack(monster3);
29:
30:
//魔剑攻击
31:
role.Weapon=newMagicSword();
32:
role.Attack(monster3);
33:
role.Attack(monster4);
34:
role.Attack(monster4);
35:
role.Attack(monster4);
36:
role.Attack(monster4);
37:
role.Attack(monster4);
38:
39:
Console.ReadLine();
40:
}
41:
}
42:
}
编译运行以上代码,得到的运行结果与上一版本代码基本一致。
1.4小李的小结
Peter显然对改进后的代码比较满意,他让小李对照两份设计和代码,进行一个小结。
小李简略思考了一下,并结合小于对一次设计指出的不足,说道:
“我认为,改进后的代码有如下优点:
第一,虽然类的数量增加了,但是每个类中方法的代码都非常短,没有了以前Attack方法那
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 依赖 注入 那些 事儿