六大設計原則【單一職責】【里氏替換】【 迪米特法則】
設計模式:面嚮物件語言開發過程中,遇到種種的場景和問題,提出的解決方案和思路,沉澱下來,設計模式是解決具體問題的套路
設計模式六大原則:面嚮物件語言開發過程中,推薦的一些指導性原則,這些是沒有明確的招數的,而且也經常被忽視或者違背!
一:單一職責原則(Single Responsibility Principle)
單一職責原則就是一個類只負責一件事兒,面嚮物件語言開發,類就是一個最基本的單位,單一職責的原則就是封裝的粒度,主要關注的單個類實現的功能
比如我們下面的例子
1 /// <summary> 2 /// 封裝 3 /// 動物類 4 /// 簡單意味著穩定5 /// </summary> 6 public class Animal 7 { 8 private string _Name = null; 9 public Animal(string name) 10 { 11 this._Name = name; 12 } 13 14 //應該拆分了 15 public void Action() 16 { 17 if (this._Name.Equals("雞")) 18 Console.WriteLine($"{this._Name} flying"); 19 else if (this._Name.Equals("牛")) 20 Console.WriteLine($"{this._Name} walking"); 21 else if (this._Name.Equals("魚")) 22 Console.WriteLine($"{this._Name} Swimming"); 23 else if (this._Name.Equals("蚯蚓")) 24 Console.WriteLine($"{this._Name} Crawling"); 25 } 26 }
我們宣告一個動物,但是每個動物的action是不一樣的,順著我們的思維模式,我們會在action中增加對應的if來判斷,不同的動物有不同的動作,
類似於這樣的,在一個方法中寫分支判斷,然後執行不同的邏輯,這就違背了單一職責原則,但是其實我們想要的功能完全能實現,如果種類比較少且不變的情況下,我們完全可以這樣操作,但是如果種類比較多且經常容易發生改變,那我們這樣寫就有很大的隱患,因為其中的改變有可能會影響到其它的。
我們可以對其進行改變,比如我們可以先建立一個基類
1 public abstract class AbstractAnimal 2 { 3 protected string _Name = null; 4 public AbstractAnimal(string name) 5 { 6 this._Name = name; 7 } 8 9 public abstract void Breath(); 10 public abstract void Action(); 11 }
然後可以建立不同動物的類來繼承於這個基類
public class Fish : AbstractAnimal { public Fish() : base("魚") { } public override void Breath() { Console.WriteLine($"{base._Name} 呼吸水"); } public override void Action() { Console.WriteLine($"{base._Name} swimming"); } }
public class Chicken : AbstractAnimal { public Chicken() : base("雞") { } public override void Breath() { Console.WriteLine($"{base._Name} 呼吸空氣"); } public override void Action() { Console.WriteLine($"{base._Name} flying"); } }
類似於這樣的,然後在自己的類中實現自己的方法,一個類只負責自己的事情,且都比較單一,簡單意味著穩定,意味著強大,這就是所謂的單一職責原則,那麼究竟什麼時候回使用單一職責原則呢?如果型別複雜,方法多,這樣建議使用單一職責原則!
那麼使用單一職責原則也有自己的弊端,具體分為以下兩個方面
1:程式碼量的增加(拆分開類的程式碼明顯比之前增加)
2:使用成本就是所謂的理解成本增高(呼叫者要曉得不同的類)
具體的單一原則分為以下五種
1:方法級別的單一職責原則:一個方法只負責一件事兒(職責分拆小方法,分支邏輯分拆)
2:類級別的單一職責原則:一個類只負責一件事兒
3:類庫級別的單一職責原則:一個類庫應該職責清晰
4:專案級別的單一職責原則:一個專案應該職責清晰(客戶端/管理後臺/後臺服務/定時任務/分散式引擎)
5:系統級別的單一職責原則:為通用功能拆分系統(IP定位/日誌/線上統計)
二: 里氏替換原則(Liskov Substitution Principle)
任何使用基類的地方,都可以透明的使用其子類,這主要是指 繼承+透明(安全,不會出現行為不一致)
繼承:子類擁有父類的一切屬性和行為,任何父類出現的地方,都可以用子類來代替,主要是因為:
1:父類有的,子類是必須有的(私有不繼承);如果出現了子類沒有的東西,那麼就應該斷掉繼承;、
2:子類可以有自己的屬性和行為,但是子類出現的地方,父類不一定能代替
3:父類實現的東西,子類就不要再寫了,(就是不要new隱藏),如果想修改父類的行為,通過abstract/virtual
舉個例子:
1 public class People 2 { 3 public int Id { get; set; } 4 public string Name { get; set; } 5 7 public void Traditional() 8 { 9 Console.WriteLine("仁義禮智信 溫良恭儉讓 "); 10 } 11 } 12 13 public class Chinese : People 14 { 15 public string Kuaizi { get; set; } 16 public void SayHi() 17 { 18 Console.WriteLine("早上好,吃了嗎?"); 19 } 20 21 } 22 23 public class Hubei : Chinese 24 { 25 public string Majiang { get; set; } 26 public new void SayHi() 27 { 28 Console.WriteLine("早上好,過早了麼?"); 29 } 30 }
呼叫的時候:
{ Chinese people = new Chinese(); people.Traditional(); people.SayHi(); } { Chinese people = new Hubei(); people.Traditional(); people.SayHi(); } { var people = new Hubei(); people.Traditional(); people.SayHi(); }
上面需要注意的是:如果是普通的方法,以左邊為主,就是左邊是什麼類,就呼叫誰的普通方法(編譯時決定),如果是abstract或者virtual則是以右邊為主(執行時決定),所以:父類有的方法,子類就不要再寫了,(就是不要new隱藏),如果想修改父類的方法,通過abstract/virtual來標識!
三:迪米特法則
也叫最少知道原則,就是:一個物件應該對其他物件保持最少的瞭解,只與直接的朋友通訊。
他主要的職責就是關注類與類之間的互動,降低類與類之間的耦合,儘量避免依賴更多的型別
舉例說明:
有一個學生類,班級類,學校類
/// <summary> /// 學生 /// </summary> public class Student { public int Id { get; set; } public string StudentName { get; set; } public int Height { private get; set; } public int Salay; public void ManageStudent() { Console.WriteLine(" {0}Manage {1} ", this.GetType().Name, this.StudentName); } }
/// <summary> /// 班級 /// </summary> public class Class { public int Id { get; set; } public string ClassName { get; set; } public List<Student> StudentList { get; set; } public void ManageClass() { Console.WriteLine(" {0}Manage {1} ", this.GetType().Name, this.ClassName); foreach (Student s in this.StudentList) { s.ManageStudent(); } } }
1 /// <summary> 2 /// 學校 3 /// </summary> 4 public class School 5 { 6 public int Id { get; set; } 7 public string SchoolName { get; set; } 8 public List<Class> ClassList { get; set; } 9 10 public void Manage() 11 { 12 Console.WriteLine("Manage {0}", this.GetType().Name); 13 foreach (Class c in this.ClassList) 14 { 15 //遵循了迪米特,school直接跟自己的朋友classList通訊,而不是跟自己的朋友的朋友通訊 16 c.ManageClass(); 17 18 #region 違背了迪米特法則(跟自己的朋友的朋友通訊) 19 //List<Student> studentList = c.StudentList; 20 //foreach (Student s in studentList) 21 //{ 22 // Console.WriteLine(" {0}Manage {1} ", s.GetType().Name, s.StudentName); 23 //} 24 #endregion 25 26 } 27 } 28
現在的關係是:一個學校有多個班級,每個班級有多個學生,現在學校想要管理學生,學校可以直接跟班級通訊,然後班級跟學生通訊,這就是所謂的只與直接朋友通訊,然後避免依賴更多型別(這個型別不包含:基類庫BCL--框架內建)
其實類與類之間的關係可以總結為:
1:縱向:繼承≈實現(最密切)
2:橫向:聚合> 組合> 關聯> 依賴(出現在方法內部)
依賴別人更少,也讓別人瞭解更少,比如我們專案中經常中用到的一些訪問修飾符:
Private:私有
Protected:子類才能獲取到,子類才能訪問到
Internal :當前dll才能看見
Public:公開得
Protected internal 疊加要麼是子類, 要麼是相同類庫
其實專案中能體現迪米特法則的地方比如:三層架構(UI-BLL-DAL),還有我們習慣建立的中間層(UI-中間層--(呼叫不同的業務邏輯進行組合)),另外還有門面模式
另外需要注意的是:單一職責法則只關注單類的功能;迪米特法則關注的是類與類之間的聯絡