設計模式(Design pattern)是一套被反復使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。使用設計模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。 毫無疑問,設計模式于己于他人于系統都是多贏的;設計模式使代碼編制真正工程化;設計模式是軟件工程的基石脈絡,如同大廈的結構一樣。
借用并改編一下魯迅老師《故鄉》中的一句話,一句話概括設計模式: 希望本無所謂有,無所謂無.這正如coding的設計模式,其實coding本沒有設計模式,用的人多了,也便成了設計模式
設計模式(面向對象)有六大原則:
開閉原則具有理想主義的色彩,它是面向對象設計的終極目標。其他幾條,則可以看做是開閉原則的實現方法。 設計模式就是實現了這些原則,從而達到了代碼復用、增加可維護性的目的。
1.概念:
2.模擬場景:
3.Solution:
4.注意事項:
5.開閉原則的優點:
6.開閉原則圖解:
1.概述: 派生類(子類)對象能夠替換其基類(父類)對象被調用
2.概念:
3.子類為什么可以替換父類的位置?:
4.C# 里氏代換原則優點:
5.C# 里氏代換原則Demo:
代碼正文:
//------------------------------------------------------------------------------// <copyright file="Program.cs" company="CNBlogs Corporation">// Copyright (C) 2015-2016 All Rights Reserved// 原博文地址: http://www.survivalescaperooms.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.C# 依賴倒轉原則用處:
3.注意事項:
4.模擬場景:
場景:
假設現在需要一個Monitor工具,去運行一些已有的APP,自動化來完成我們的工作。Monitor工具需要啟動這些已有的APP,并且寫下Log。
代碼實現1:
//------------------------------------------------------------------------------// <copyright file="Dependency.cs" company="CNBlogs Corporation">// Copyright (C) 2015-2016 All Rights Reserved// 原博文地址: http://www.survivalescaperooms.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.survivalescaperooms.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.survivalescaperooms.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.survivalescaperooms.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.survivalescaperooms.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.survivalescaperooms.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.survivalescaperooms.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.survivalescaperooms.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.survivalescaperooms.com/toutou/
關于作者:專注于微軟平臺的項目開發。如有問題或建議,請多多賜教!
版權聲明:本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。
特此聲明:所有評論和私信都會在第一時間回復。也歡迎園子的大大們指正錯誤,共同進步。或者直接私信我
聲援博主:如果您覺得文章對您有幫助,可以點擊文章右下角【推薦】一下。您的鼓勵是作者堅持原創和持續寫作的最大動力!
新聞熱點
疑難解答