描述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 版權區