描述html
本篇文章主要讲解 :编程
(1)OO设计OCP原则;segmentfault
(2)依赖注入引入设计模式
(3)依赖注入分析架构
(4)依赖注入种类app
1 内容区框架
1.1 IOC背景dom
(1)Ralph E. Johnson & Brian Foote 论文 《Designing Reusable Classes》学习
早在1988年,Ralph E. Johnson & Brian Foote在论文Designing Reusable Classes中写到:测试
《OO面向对象设计七大原则,》,请参照我另一篇文章 OO面向对象设计七大原则 .
2 OCP分析
OCP原则(Open Close Principle),核心思想是封闭修改(隔离变化),支持扩展(继承,目的是复用)。
为了分析清楚OCP,咱们这里以人为研究对象,即把人看成超类。
2.1 定义超类(People类)
在定义一个类时,主要关心类的特性(Class 中的属性)和行为(Class 中的方法),这里,咱们假设超类People中存在以下属性和方法:
a.属性:头,嘴
b.方法:Eat(),Sleep(),WalkPosture()
1 public abstract class People 2 { 3 private string Head;//头 4 private string Mouse;//嘴 5 6 public void Eat() //吃饭 7 { 8 //...... 9 } 10 public void Sleep() //睡觉 11 { 12 //...... 13 } 14 15 public abstract void WalkPosture(); //每一个人的走路姿式不同 16 17 18 }
UML类图以下:
(1)咱们向People类中添加Speak()方法,使其可以说汉语(普通话),则People类变为以下:
1 public class People 2 { 3 private string Head;//头 4 private string Mouse;//嘴 5 6 public void Eat() //吃饭 7 { 8 //...... 9 } 10 public void Sleep() //睡觉 11 { 12 //...... 13 } 14 15 public abstract void WalkPosture();//每一个人的走路姿式不同 16
17 18 public string SpeakLanguage() //说话 19 { 20 //普通话 21 } 22 23 }
此时,UML类图变为以下:
(2)具体的某我的,继承People类便可。
1 public class XiaoMing : People 2 { 3 //...... 4 }
UML图以下:
2.2 对People类分析
People类UML图以下:
分析:
假设这样一个情景:即People类中不只仅是中国人,还有其余231个国家的人(每一个国家的语言并不彻底相同),咱们在本程序中,加入英国人,俄罗斯人,即People类中只有中国人,英国人,俄罗斯人三个国家的人。
作法一:
在People类中改写SpeakLanguage()方法。
1 public class People 2 { 3 private string Head;//头 4 private string Mouse;//嘴 5 6 public void Eat() //吃饭 7 { 8 //...... 9 } 10 public void Sleep() //睡觉 11 { 12 //...... 13 } 14 15 public abstract WalkPosture();//每一个人的走路姿式不同 16 17 18 public Language SpeakLanguage( Language language) //说话 19 { 20 if (language=="Chinese") 21 { 22 //普通话 23 } 24 if (language=="English") 25 { 26 //English 27 } 28 else 31 { 29 //Russian 30 } 31 32 } 33 34 }
咱们来分析一下作法一的
问题:
Q1:因为直接修改超类People中的方法,违背了OO软件设计开闭原则(Open Close Principle,简称OCP);
Q2:若是再把其余国家加进来,那么SpeakLanguage() 方法体 会有不少 if.....else.....,不利于代码维护;
方法二:
根据OCP原则,对修改关闭,对扩展开放;在超类People中:
(1)属性Head,Mouse,每一个人都具备;
(2)方法Eat(),Sleep(),每一个人都具备;
(3)方法WalkPosture(),每一个人走路的姿式不同,能够用抽象方法来实现;
(4)方法SpeakLanguage(Language language),每一个国籍的人,说话的语言不必定相同,这是类中变化的部分,须要独立开来;
所以,能够改写为以下:
定义一个Language类
Language类
1 public class Language 2 { 3 //To add Language business codes 4 }
People类
1 public class People 2 { 3 private string Head;//头 4 private string Mouse;//嘴 5 6 public void Eat() //吃饭 7 { 8 //...... 9 } 10 public void Sleep() //睡觉 11 { 12 //...... 13 } 14 15 public abstract WalkPosture();//每一个人的走路姿式不同 16 17 }
接口 ILanguage
1 public interface ILanguage 2 { 3 Language SpeakLanguage(Language language); 4 }
中国人
1 public class Chinese : People,ILanguage 2 { 3 // 继承People 4 // WalkPostrue 5 // 实现接口方法 Language SpeakLanguage(Language language); 6 }
英国人
1 public class English : People,ILanguage 2 { 3 // 继承People 4 // WalkPostrue 5 // 实现接口方法 Language SpeakLanguage(Language language); 6 }
俄罗斯人
1 public class Chinese : People,ILanguage 2 { 3 // 继承People 4 // WalkPostrue 5 // 实现接口方法 Language SpeakLanguage(Language language); 6 }
UML关系图以下:
若是你能将本OCP例子改成依赖注入,那么你没必要往下看了,由于你已经会了。
3 依赖注入
在对依赖注入简要概述和对OCP简要分析以后,咱们来研究依赖注入。
3.1 例子:(引用)
一个叫IGame的游戏公司,正在开发一款ARPG游戏(动做&角色扮演类游戏,如魔兽世界、梦幻西游这一类的游戏)。通常这类游戏都有一个基本的功能,就是打怪(玩家攻击怪物,借此得到经验、虚拟货币和虚拟装备),而且根据玩家角色所装备的武器不一样,攻击效果也不一样.打怪功能中的某一个功能:
(1)、角色可向怪物实施攻击,一次攻击后,怪物掉部分HP,HP掉完后,怪物死亡。
(2)、角色可装配不一样武器,有木剑、铁剑、魔剑。
(3)、木剑每次攻击,怪物掉20PH,铁剑掉50HP,魔剑掉100PH。
IAttackStrategy接口
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace IGameLiAdv 7 { 8 internal interface IAttackStrategy 9 { 10 void AttackTarget(Monster monster); 11 } 12 }
WoodSword类
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace IGameLiAdv 7 { 8 internal sealed class WoodSword : IAttackStrategy 9 { 10 public void AttackTarget(Monster monster) 11 { 12 monster.Notify(20); 13 } 14 } 15 }
IronSword类
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace IGameLiAdv 7 { 8 internal sealed class IronSword : IAttackStrategy 9 { 10 public void AttackTarget(Monster monster) 11 { 12 monster.Notify(50); 13 } 14 } 15 }
MagicSword类
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace IGameLiAdv 7 { 8 internal sealed class MagicSword : IAttackStrategy 9 { 10 private Random _random = new Random(); 11 12 public void AttackTarget(Monster monster) 13 { 14 Int32 loss = (_random.NextDouble() < 0.5) ? 100 : 200; 15 if (200 == loss) 16 { 17 Console.WriteLine("出现暴击!!!"); 18 } 19 monster.Notify(loss); 20 } 21 } 22 }
Monster类
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace IGameLiAdv 7 { 8 /// <summary> 9 /// 怪物 10 /// </summary> 11 internal sealed class Monster 12 { 13 /// <summary> 14 /// 怪物的名字 15 /// </summary> 16 public String Name { get; set; } 17 18 /// <summary> 19 /// 怪物的生命值 20 /// </summary> 21 private Int32 HP { get; set; } 22 23 public Monster(String name,Int32 hp) 24 { 25 this.Name = name; 26 this.HP = hp; 27 } 28 29 /// <summary> 30 /// 怪物被攻击时,被调用的方法,用来处理被攻击后的状态更改 31 /// </summary> 32 /// <param name="loss">这次攻击损失的HP</param> 33 public void Notify(Int32 loss) 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 }
Role类
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace IGameLiAdv 7 { 8 /// <summary> 9 /// 角色 10 /// </summary> 11 internal sealed class Role 12 { 13 /// <summary> 14 /// 表示角色目前所持武器 15 /// </summary> 16 public IAttackStrategy Weapon { get; set; } 17 18 /// <summary> 19 /// 攻击怪物 20 /// </summary> 21 /// <param name="monster">被攻击的怪物</param> 22 public void Attack(Monster monster) 23 { 24 this.Weapon.AttackTarget(monster); 25 } 26 } 27 }
Program类
1 namespace IGameLiAdv 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 //生成怪物 8 Monster monster1 = new Monster("小怪A", 50); 9 Monster monster2 = new Monster("小怪B", 50); 10 Monster monster3 = new Monster("关主", 200); 11 Monster monster4 = new Monster("最终Boss", 1000); 12 13 //生成角色 14 Role role = new Role(); 15 16 //木剑攻击 17 role.Weapon = new WoodSword(); 18 role.Attack(monster1); 19 20 //铁剑攻击 21 role.Weapon = new IronSword(); 22 role.Attack(monster2); 23 role.Attack(monster3); 24 25 //魔剑攻击 26 role.Weapon = new MagicSword(); 27 role.Attack(monster3); 28 role.Attack(monster4); 29 role.Attack(monster4); 30 role.Attack(monster4); 31 role.Attack(monster4); 32 role.Attack(monster4); 33 34 Console.ReadLine(); 35 } 36 } 37 }
UML关系图
3.2 分析:
引入Strategy模式后,不但消除了重复性代码,更重要的是,使得设计符合了OCP。若是之后要加一个新武器,只要新建一个类,实现IAttackStrategy接口,当角色须要装备这个新武器时,客户代码只要实例化一个新武器类,并赋给Role的Weapon成员就能够了,已有的Role和Monster代码都不用改动。这样就实现了对扩展开发,对修改关闭。
上面例子的第二种实现中,Role不依赖具体武器,而仅仅依赖一个IAttackStrategy接口,接口是不能实例化的,虽然Role的Weapon成员类型定义为IAttackStrategy,但最终仍是会被赋予一个实现了IAttackStrategy接口的具体武器,而且随着程序进展,一个角色会装备不一样的武器,从而产生不一样的效用。赋予武器的职责,在Demo中是放在了测试代码里。
这里,测试代码实例化一个具体的武器,并赋给Role的Weapon成员的过程,就是依赖注入!这里要清楚,依赖注入实际上是一个过程的称谓!
依赖注入产生的背景:
随着面向对象分析与设计的发展,一个良好的设计,核心原则之一就是将变化隔离,使得变化部分发生变化时,不变部分不受影响(这也是OCP的目的)。为了作到这一点,要利用面向对象中的多态性,使用多态性后,客户类再也不直接依赖服务类,而是依赖于一个抽象的接口,这样,客户类就不能在内部直接实例化具体的服务类。可是,客户类在运做中又客观须要具体的服务类提供服务,由于接口是不能实例化去提供服务的。就产生了“客户类不许实例化具体服务类”和“客户类须要具体服务类”这样一对矛盾。为了解决这个矛盾,开发人员提出了一种模式:客户类(如上例中的Role)定义一个注入点(Public成员Weapon),用于服务类(实现IAttackStrategy的具体类,如WoodSword、IronSword和MagicSword,也包括之后加进来的全部实现IAttackStrategy的新类)的注入,而客户类的客户类(Program,即测试代码)负责根据状况,实例化服务类,注入到客户类中,从而解决了这个矛盾。
3.3 依赖注入的正式定义:
依赖注入(Dependency Injection),是这样一个过程:因为某客户类只依赖于服务类的一个接口,而不依赖于具体服务类,因此客户类只定义一个注入点。在程序运行过程当中,客户类不直接实例化具体服务类实例,而是客户类的运行上下文环境或专门组件负责实例化服务类,而后将其注入到客户类中,保证客户类的正常运行。
3.4 依赖注入总结
(1)组成要素
a.接口及其实现(剥离变化)
b.客户类和服务类
(2)核心思想
a.延迟注入服务,并非一开始就注入服务,即在用到时,才经过接口形式注入服务;
4 依赖注入的种类
依赖注入大体可分为以下种类:
限于篇幅的限制,依赖注入种类分析,将在之后的文章中与你们分享。
5 参考文献
【01】https://segmentfault.com/a/1190000010456858
【02】Head First设计模式
6 版权区