設計模式:面嚮對象語言開發過程當中,遇到種種的場景和問題,提出的解決方案和思路,沉澱下來,設計模式是解決具體問題的套路編程
設計模式六大原則:面嚮對象語言開發過程當中,推薦的一些指導性原則,這些是沒有明確的招數的,並且也常常被忽視或者違背!設計模式
一:單一職責原則(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來判斷,不一樣的動物有不一樣的動做,iphone
相似於這樣的,在一個方法中寫分支判斷,而後執行不一樣的邏輯,這就違背了單一職責原則,可是其實咱們想要的功能徹底能實現,若是種類比較少且不變的狀況下,咱們徹底能夠這樣操做,可是若是種類比較多且常常容易發生改變,那咱們這樣寫就有很大的隱患,由於其中的改變有可能會影響到其它的。分佈式
咱們能夠對其進行改變,好比咱們能夠先建立一個基類ide
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 }
而後能夠建立不一樣動物的類來繼承於這個基類this
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"); } }
相似於這樣的,而後在本身的類中實現本身的方法,一個類只負責本身的事情,且都比較單一,簡單意味着穩定,意味着強大,這就是所謂的單一職責原則,那麼究竟何時回使用單一職責原則呢?若是類型複雜,方法多,這樣建議使用單一職責原則!spa
那麼使用單一職責原則也有本身的弊端,具體分爲如下兩個方面
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-中間層--(調用不一樣的業務邏輯進行組合)),另外還有門面模式
另外須要注意的是:單一職責法則只關注單類的功能;迪米特法則關注的是類與類之間的聯繫
四:依賴倒置原則(Dependence Inversion Principle)
依賴倒置原則:高層模塊不該該依賴於低層模塊,兩者應該經過抽象依賴
那何爲高層何爲底層,通常來講使用者爲高層,被調用者爲低層,具體仍是舉例說明,咱們先建立一個手機:
public abstract class AbstractPhone { public int Id { get; set; } public string Branch { get; set; } public abstract void Call(); public abstract void Text(); } public class iPhone : AbstractPhone { public override void Call() { Console.WriteLine("User {0} Call", this.GetType().Name); } public override void Text() { Console.WriteLine("User {0} Call", this.GetType().Name); } }
接着咱們建立一個學生,而後學生玩手機:
1 public class Student 2 { 3 public int Id { get; set; } 4 public string Name { get; set; } 5 6 /// <summary> 7 /// 依賴細節 高層就依賴了底層 8 /// </summary> 9 /// <param name="phone"></param> 10 public void PlayiPhone(iPhone phone) 11 { 12 Console.WriteLine("這裏是{0}", this.Name); 13 phone.Call(); 14 phone.Text(); 15 } 16 17 public void PlayLumia(Lumia phone) 18 { 19 Console.WriteLine("這裏是{0}", this.Name); 20 phone.Call(); 21 phone.Text(); 22 } 23 24 public void PlayHonor(Honor phone) 25 { 26 Console.WriteLine("這裏是{0}", this.Name); 27 phone.Call(); 28 phone.Text(); 29 } 30 }
而後上面的學生就是高層,而手機就是低層,而後學生玩手機不該該直接直接寫PlayiPhone,PlayLumia,PlayHonor 等,顯然這是依賴的細節,若是這樣寫,之後增長一個手機,則要在學生類中增長了一個play方法,這樣就會使學生類反覆修改而不穩定,因此咱們通常會定義一個基類爲AbstractPhone,而後把各個型號通用的手機屬性和功能都寫在基類中,而後在學生中增長一個Play的方法以下:
1 public void Play(AbstractPhone phone) 2 { 3 Console.WriteLine("這裏是{0}", this.Name); 4 phone.Call(); 5 phone.Text(); 6 }
這樣的話之後增長手機的話只要繼承AbstractPhone,則在外部均可以直接調用。由於父類出現的地方均可以用子類代替。
而後有人還說這個能夠直接使用泛型來寫以下:
public void PlayT<T>(T phone) where T : AbstractPhone { Console.WriteLine("這裏是{0}", this.Name); phone.Call(); phone.Text(); }
這個T使用基類來進行約束,其實就等同於用父類參數類型!
調用的方法以下:
1 { 2 iPhone phone = new iPhone(); 3 student.PlayiPhone(phone); 4 student.PlayT(phone); 5 student.Play(phone); 6 } 7 { 8 Lumia phone = new Lumia(); 9 student.PlayLumia(phone); 10 student.PlayT(phone); 11 student.Play(phone); 12 } 13 { 14 Honor phone = new Honor(); 15 student.PlayHonor(phone); 16 student.PlayT(phone); 17 student.Play(phone); 18 }
而後這樣寫的好處主要分爲如下兩點:
1 :一個方法知足不一樣類型的參數
2:還支持擴展,只要是實現了這個抽象,不用修改Student,穩定的同時又多了擴展
這就是所謂的面向抽象編程,可是面向抽象編程只適合於通用功能,若是你想要在子類中有特殊的操做,好比想要在iphone手機新增一個其它手機沒有的方法A,而後play方法還傳入AbstractPhone這個參數,這樣是麼有辦法訪問到A,這就是非通用的,就不該該面向抽象
那何爲面向抽象,以及面向抽象的好處?
面向抽象就是:只要抽象不變,高層就不變!
面向抽象的好處:
面嚮對象語言開發,就是類與類之間進行交互,若是高層直接依賴低層的細節,細節是多變的,那麼低層的變化就致使上層的變化;若是層數多了,底層的修改會直接水波效應傳遞到最上層,一點細微的改動都會致使整個系統從下往上的修改!
而面向抽象,若是高層和低層沒有直接依賴,而是依賴於抽象,抽象通常是穩定的,那低層細節的變化擴展就不會影響到高層,這樣就能支持層內部的橫向擴展,不會影響其餘地方,這樣的程序架構就是穩定的
之後不少的設計模式都會跟抽象有關,好比咱們常常說的控制反轉IOC就是一個很好的例子!依賴倒置原則(理論基礎)---IOC控制反轉(實踐封裝)---DI依賴注入(實現IOC的手段)!
五: 接口隔離原則(Interface Segregation Principle)
接口隔離原則則是:客戶端不該該依賴它不須要的接口, 一個類對另外一個類的依賴應該創建在最小的接口上;
這個原則跟本身實際工做有很大的關係,總結下來能夠經過如下幾點來定義接口:
1: 既不能是大而全,會強迫實現沒有的東西,也會依賴本身不須要的東西
2 :也不能一個方法一個接口,這樣面向抽象也沒有意義的
按照功能的密不可分來定義接口,
並且應該是動態的,隨着業務發展會有變化的,可是在設計的時候,要留好提早量,避免抽象的變化
沒有標準答案,隨着業務和產品來調整的
3 :接口合併 Map--定位/搜索/導航 這種屬於固定步驟,業務細節,儘可能的內聚,在接口也不要暴露太多業務細節(就是密切相連的能夠放在一個接口來實現,而不是分多個接口實現功能)
六:開閉原則(Open Closed Principle)
開閉原則:對擴展開發,對修改關閉
開閉原則只是一個目標,並無任何的手段,也被稱之爲總則,其餘5個原則的建議,就是爲了更好的作到OCP, 開閉原則也是面嚮對象語言開發一個終極目標!
若是有功能增長/修改的需求能夠經過優先級考慮如下修改:A:修改現有方法---B:增長方法---C:增長類---D:增長/替換類庫