設計模式(Design pattern)是一套被反覆使用、多數人知曉的、通過分類編目的、代碼設計經驗的總結。使用設計模式是爲了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。 毫無疑問,設計模式於己於他人於系統都是多贏的;設計模式使代碼編制真正工程化;設計模式是軟件工程的基石脈絡,如同大廈的結構同樣。javascript
借用並改編一下魯迅老師《故鄉》中的一句話,一句話歸納設計模式: 但願本無所謂有,無所謂無.這正如coding的設計模式,其實coding本沒有設計模式,用的人多了,也便成了設計模式java
設計模式(面向對象)有六大原則:ios
開閉原則具備理想主義的色彩,它是面向對象設計的終極目標。其餘幾條,則能夠看作是開閉原則的實現方法。 設計模式就是實現了這些原則,從而達到了代碼複用、增長可維護性的目的。數據庫
1.概念: 編程
2.模擬場景: 設計模式
3.Solution: app
4.注意事項: ide
5.開閉原則的優勢: 函數
6.開閉原則圖解: 工具
1.概述: 派生類(子類)對象可以替換其基類(父類)對象被調用
2.概念:
3.子類爲何能夠替換父類的位置?:
4.里氏代換原則優勢:
5.里氏代換原則Demo:
代碼正文:
//------------------------------------------------------------------------------ // <copyright file="Program.cs" company="CNBlogs Corporation"> // Copyright (C) 2015-2016 All Rights Reserved // 原博文地址: http://www.cnblogs.com/toutou/ // 做 者: 請叫我頭頭哥 // </copyright> //------------------------------------------------------------------------------ namespace TestApp { using System; class Program { static void Main(string[] args) { Transportation transportation = new Transportation(); transportation.Say(); Transportation sedan = new Sedan(); sedan.Say(); Console.ReadKey(); } } class Transportation { public Transportation() { Console.WriteLine("Transportation?"); } public virtual void Say() { Console.WriteLine("121"); } } class Sedan:Transportation { public Sedan() { Console.WriteLine("Transportation:Sedan"); } public override void Say() { Console.WriteLine("Sedan"); } } class Bicycles : Transportation { public Bicycles() { Console.WriteLine("Transportation:Bicycles"); } public override void Say() { Console.WriteLine("Bicycles"); } } }
代碼效果:
6.里氏代換原則圖解:
1.概念:
2.依賴倒轉原則用處:
3.注意事項:
4.模擬場景:
場景:
假設如今須要一個Monitor工具,去運行一些已有的APP,自動化來完成咱們的工做。Monitor工具須要啓動這些已有的APP,而且寫下Log。
代碼實現1:
//------------------------------------------------------------------------------ // <copyright file="Dependency.cs" company="CNBlogs Corporation"> // Copyright (C) 2015-2016 All Rights Reserved // 原博文地址: http://www.cnblogs.com/toutou/ // 做 者: 請叫我頭頭哥 // </copyright> //------------------------------------------------------------------------------ namespace TestLibrary.ExtensionsClass { using System; public class AppOne { public bool Start() { Console.WriteLine("1號APP開始啓動"); return true; } public bool ExportLog() { Console.WriteLine("1號APP輸出日誌"); return true; } } public class AppTwo { public bool Start() { Console.WriteLine("2號APP開始啓動"); return true; } public bool ExportLog() { Console.WriteLine("2號APP輸出日誌"); return true; } } public class Monitor { public enum AppNumber { AppOne=1, AppTwo=2 } private AppOne appOne = new AppOne(); private AppTwo appTwo = new AppTwo(); private AppNumber number; public Monitor(AppNumber number) { this.number = number; } public bool StartApp() { return number == AppNumber.AppOne ? appOne.Start() : appTwo.Start(); } public bool ExportAppLog() { return number == AppNumber.AppOne ? appOne.ExportLog() : appTwo.ExportLog(); } } }
代碼解析1:
在代碼實現1中咱們已經輕鬆實現了Monitor去運行已有APP而且寫下LOG的需求。而且代碼已經上線了.
春...夏...秋...冬...
春...夏...秋...冬...
春...夏...秋...冬...
就這樣,三年過去了。
一天客戶找上門了,公司業務擴展了,如今須要新加3個APP用Monitor自動化。這樣咱們就必須得改Monitor。
代碼實現2:
//------------------------------------------------------------------------------ // <copyright file="Dependency.cs" company="CNBlogs Corporation"> // Copyright (C) 2015-2016 All Rights Reserved // 原博文地址: http://www.cnblogs.com/toutou/ // 做 者: 請叫我頭頭哥 // </copyright> //------------------------------------------------------------------------------ namespace TestLibrary.ExtensionsClass { using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; public class Monitor { public enum AppNumber { AppOne = 1, AppTwo = 2, AppThree = 3, AppFour = 4, AppFive = 5 } private AppOne appOne = new AppOne(); private AppTwo appTwo = new AppTwo(); private AppThree appThree = new AppThree(); private AppFour appFour = new AppFour(); private AppFive appFive = new AppFive(); private AppNumber number; public Monitor(AppNumber number) { this.number = number; } public bool StartApp() { bool result = false; if (number == AppNumber.AppOne) { result = appOne.Start(); } else if (number == AppNumber.AppTwo) { result = appTwo.Start(); } else if (number == AppNumber.AppThree) { result = appThree.Start(); } else if (number == AppNumber.AppFour) { result = appFour.Start(); } else if (number == AppNumber.AppFive) { result = appFive.Start(); } return result; } public bool ExportAppLog() { bool result = false; if (number == AppNumber.AppOne) { result = appOne.ExportLog(); } else if (number == AppNumber.AppTwo) { result = appTwo.ExportLog(); } else if (number == AppNumber.AppThree) { result = appThree.ExportLog(); } else if (number == AppNumber.AppFour) { result = appFour.ExportLog(); } else if (number == AppNumber.AppFive) { result = appFive.ExportLog(); } return result; } } }
代碼解析2:
這樣會給系統添加新的相互依賴。而且隨着時間和需求的推移,會有更多的APP須要用Monitor來監測,這個Monitor工具也會被愈來愈對的if...else撐爆炸,並且代碼隨着APP越多,越難維護。最終會致使Monitor走向滅亡(下線)。
介於這種狀況,能夠用Monitor這個模塊來生成其它的程序,使得系統可以用在須要的APP上。OOD給咱們提供了一種機制來實現這種「依賴倒置」。
代碼實現3:
//------------------------------------------------------------------------------ // <copyright file="Dependency.cs" company="CNBlogs Corporation"> // Copyright (C) 2015-2016 All Rights Reserved // 原博文地址: http://www.cnblogs.com/toutou/ // 做 者: 請叫我頭頭哥 // </copyright> //------------------------------------------------------------------------------ namespace TestLibrary.ExtensionsClass { using System; public interface IApp { bool Start(); bool ExportLog(); } public class AppOne : IApp { public bool Start() { Console.WriteLine("1號APP開始啓動"); return true; } public bool ExportLog() { Console.WriteLine("1號APP輸出日誌"); return true; } } public class AppTwo : IApp { public bool Start() { Console.WriteLine("2號APP開始啓動"); return true; } public bool ExportLog() { Console.WriteLine("2號APP輸出日誌"); return true; } } public class Monitor { private IApp iapp; public Monitor(IApp iapp) { this.iapp = iapp; } public bool StartApp() { return iapp.Start(); } public bool ExportAppLog() { return iapp.ExportLog(); } } }
代碼解析3:
如今Monitor依賴於IApp這個接口,而與具體實現的APP類沒有關係,因此不管再怎麼添加APP都不會影響到Monitor自己,只須要去添加一個實現IApp接口的APP類就能夠了。
1.概念:
2.含義:
3.模擬場景:
4.代碼演示:
//------------------------------------------------------------------------------ // <copyright file="Dependency.cs" company="CNBlogs Corporation"> // Copyright (C) 2015-2016 All Rights Reserved // 原博文地址: http://www.cnblogs.com/toutou/ // 做 者: 請叫我頭頭哥 // </copyright> //------------------------------------------------------------------------------ namespace TestLibrary.ExtensionsClass { using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; public interface IReview { void ReviewWorkFlow(); void RejectWorkFlow(); } public class Review : IReview { public void ReviewWorkFlow() { Console.WriteLine("開始審覈工做流"); } public void RejectWorkFlow() { Console.WriteLine("已經駁回工做流"); } } public interface ISubmit { void SubmitWorkFlow(); void CancelWorkFlow(); } public class Submit : ISubmit { public void SubmitWorkFlow() { Console.WriteLine("開始提交工做流"); } public void CancelWorkFlow() { Console.WriteLine("已經撤銷工做流"); } } }
5.代碼解析:
其實接口隔離原則很好理解,在上面的例子裏能夠看出來,若是把OA的外部和內部都定義一個接口的話,那這個接口會很大,並且實現接口的類也會變得臃腫。
1.概念:
2.合成/聚合解析:
聚合概念:
聚合用來表示「擁有」關係或者總體與部分的關係。表明部分的對象有可能會被多個表明總體的對象所共享,並且不必定會隨着某個表明總體的對象被銷燬或破壞而被銷燬或破壞,部分的生命週期能夠超越總體。例如,Iphone5和IOS,當Iphone5刪除後,IOS還能存在,IOS能夠被Iphone6引用。
聚合關係UML類圖:
代碼演示:
//------------------------------------------------------------------------------ // <copyright file="Dependency.cs" company="CNBlogs Corporation"> // Copyright (C) 2015-2016 All Rights Reserved // 原博文地址: http://www.cnblogs.com/toutou/ // 做 者: 請叫我頭頭哥 // </copyright> //------------------------------------------------------------------------------ namespace TestLibrary.ExtensionsClass { class IOS { } class Iphone5 { private IOS ios; public Iphone5(IOS ios) { this.ios = ios; } } }
合成概念:
合成用來表示一種強得多的「擁有」關係。在一個合成關係裏,部分和總體的生命週期是同樣的。一個合成的新對象徹底擁有對其組成部分的支配權,包括它們的建立和湮滅等。使用程序語言的術語來講,合成而成的新對象對組成部分的內存分配、內存釋放有絕對的責任。一個合成關係中的成分對象是不能與另外一個合成關係共享的。一個成分對象在同一個時間內只能屬於一個合成關係。若是一個合成關係湮滅了,那麼全部的成分對象要麼本身湮滅全部的成分對象(這種狀況較爲廣泛)要麼就得將這一責任交給別人(較爲罕見)。例如:水和魚的關係,當水沒了,魚也不可能獨立存在。
合成關係UML類圖:
代碼演示:
//------------------------------------------------------------------------------ // <copyright file="Dependency.cs" company="CNBlogs Corporation"> // Copyright (C) 2015-2016 All Rights Reserved // 原博文地址: http://www.cnblogs.com/toutou/ // 做 者: 請叫我頭頭哥 // </copyright> //------------------------------------------------------------------------------ namespace TestLibrary.ExtensionsClass { using System; class Fish { public Fish CreateFish() { Console.WriteLine("一條小魚兒"); return new Fish(); } } class Water { private Fish fish; public Water() { fish = new Fish(); } public void CreateWater() { // 當建立了一個水的地方,那這個地方也得放點魚進去 fish.CreateFish(); } } }
3.模擬場景:
好比說咱們先搖到號(這個比較困難)了,須要爲本身買一輛車,若是4S店裏的車默認的配置都是同樣的。那麼咱們只要買車就會有這些配置,這時使用了繼承關係:
不可能全部汽車的配置都是同樣的,因此就有SUV和小轎車兩種(只列舉兩種比較熱門的車型),而且使用機動車對它們進行聚合使用。這時採用了合成/聚合的原則:
1.概念:
2.模擬場景:
場景:公司財務總監發出指令,讓財務部門的人去統計公司已發公司的人數。
一個常態的編程:(確定是不符LoD的反例)
UML類圖:
代碼演示:
//------------------------------------------------------------------------------ // <copyright file="Dependency.cs" company="CNBlogs Corporation"> // Copyright (C) 2015-2016 All Rights Reserved // 原博文地址: http://www.cnblogs.com/toutou/ // 做 者: 請叫我頭頭哥 // </copyright> //------------------------------------------------------------------------------ namespace TestLibrary.ExtensionsClass { using System; using System.Collections.Generic; /// <summary> /// 財務總監 /// </summary> public class CFO { /// <summary> /// 財務總監發出指令,讓財務部門統計已發工資人數 /// </summary> public void Directive(Finance finance) { List<Employee> employeeList = new List<Employee>(); // 初始化已發工資人數 for (int i = 0; i < 500; i++) { employeeList.Add(new Employee()); } // 轉告財務部門開始統計已結算公司的員工 finance.SettlementSalary(employeeList); } } /// <summary> /// 財務部 /// </summary> public class Finance { /// <summary> /// 統計已結算公司的員工 /// </summary> public void SettlementSalary(List<Employee> employeeList) { Console.WriteLine(string.Format("已結算工資人數:{0}", employeeList.Count)); } } /// <summary> /// 員工 /// </summary> public class Employee { } /// <summary> /// 主程序 /// </summary> public class Runner { public static void main(String[] args) { CFO cfo = new CFO(); // 財務總監發出指令 cfo.Directive(new Finance()); } } }
根據模擬的場景:財務總監讓財務部門總結已發工資的人數。 財務總監和員工是陌生關係(即總監不須要對員工執行任何操做)。根據上述UML圖和代碼解決辦法顯然能夠看出,上述作法違背了LoD法則。
依據LoD法則解耦:(符合LoD的例子)
UML類圖:
代碼演示:
//------------------------------------------------------------------------------ // <copyright file="Dependency.cs" company="CNBlogs Corporation"> // Copyright (C) 2015-2016 All Rights Reserved // 原博文地址: http://www.cnblogs.com/toutou/ // 做 者: 請叫我頭頭哥 // </copyright> //------------------------------------------------------------------------------ namespace TestLibrary.ExtensionsClass { using System; using System.Collections.Generic; /// <summary> /// 財務總監 /// </summary> public class CFO { /// <summary> /// 財務總監發出指令,讓財務部門統計已發工資人數 /// </summary> public void Directive(Finance finance) { // 通知財務部門開始統計已結算公司的員工 finance.SettlementSalary(); } } /// <summary> /// 財務部 /// </summary> public class Finance { private List<Employee> employeeList; //傳遞公司已工資的人 public Finance(List<Employee> _employeeList) { this.employeeList = _employeeList; } /// <summary> /// 統計已結算公司的員工 /// </summary> public void SettlementSalary() { Console.WriteLine(string.Format("已結算工資人數:{0}", employeeList.Count)); } } /// <summary> /// 員工 /// </summary> public class Employee { } /// <summary> /// 主程序 /// </summary> public class Runner { public static void main(String[] args) { List<Employee> employeeList = new List<Employee>(); // 初始化已發工資人數 for (int i = 0; i < 500; i++) { employeeList.Add(new Employee()); } CFO cfo = new CFO(); // 財務總監發出指令 cfo.Directive(new Finance(employeeList)); } } }
根據LoD原則咱們須要讓財務總監和員工之間沒有之間的聯繫。這樣纔是遵照了迪米特法則。
想搞懂設計模式,必須先知道設計模式遵循的六大原則,不管是哪一種設計模式都會遵循一種或者多種原則。這是面向對象不變的法則。本文針對的是設計模式(面向對象)主要的六大原則展開的講解,並儘可能作到結合實例和UML類圖,幫助你們理解。在後續的博文中還會跟進一些設計模式的實例。
做 者:請叫我頭頭哥
出 處:http://www.cnblogs.com/toutou/
關於做者:專一於基礎平臺的項目開發。若有問題或建議,請多多賜教!
版權聲明:本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文連接。
特此聲明:全部評論和私信都會在第一時間回覆。也歡迎園子的大大們指正錯誤,共同進步。或者直接私信我
聲援博主:若是您以爲文章對您有幫助,能夠點擊文章右下角【推薦】一下。您的鼓勵是做者堅持原創和持續寫做的最大動力!