原文地址:http://www.cnblogs.com/zhili/p/SingletonPatterm.htmlhtml
通過這段時間對設計模式的學習,本身的感觸仍是不少的,由於我如今在寫代碼的時候,常常會想一想這裏能不能用什麼設計模式來進行重構。因此,學完設計模式以後,感受它會慢慢地影響到你寫代碼的思惟方式。這裏對設計模式作一個總結,一來能夠對全部設計模式進行一個梳理,二來能夠作一個索引來幫助你們收藏。面試
PS: 其實,很早以前我就看過全部的設計模式了,可是並無寫博客,可是不久就很快忘記了,也沒有起到什麼做用,此次以博客的形式總結出來,發現效果仍是很明顯的,由於經過這種總結的方式,我對它理解更深入了,也記住的更牢靠了,也影響了本身平時實現功能的思惟。因此,我鼓勵你們能夠經過作筆記的方式來把本身學到的東西進行梳理,這樣相信能夠理解更深,更好,我也會一直寫下來,以後打算寫WCF一系列文章。算法
其實WCF內容很早也看過了,而且博客園也有不少前輩寫的很好,可是,我以爲我仍是須要本身總結,由於只有這樣,知識才是本身的,別人寫的多好,你看了以後,其實仍是別人了,因此鼓勵你們幾點(對於這幾點,也是對本身的一個提醒):數據庫
系列導航:編程
C#設計模式(1)——單例模式設計模式
C#設計模式(5)——建造者模式(Builder Pattern)app
C#設計模式(6)——原型模式(Prototype Pattern)
C#設計模式(7)——適配器模式(Adapter Pattern)
C#設計模式(8)——橋接模式(Bridge Pattern)
C#設計模式(9)——裝飾者模式(Decorator Pattern)
C#設計模式(10)——組合模式(Composite Pattern)
C#設計模式(11)——外觀模式(Facade Pattern)
C#設計模式(12)——享元模式(Flyweight Pattern)
C#設計模式(13)——代理模式(Proxy Pattern)
C#設計模式(14)——模板方法模式(Template Method)
C#設計模式(15)——命令模式(Command Pattern)
C#設計模式(16)——迭代器模式(Iterator Pattern)
C#設計模式(17)——觀察者模式(Observer Pattern)
C#設計模式(18)——中介者模式(Mediator Pattern)
C#設計模式(19)——狀態者模式(State Pattern)
C#設計模式(20)——策略者模式(Stragety Pattern)
C#設計模式(22)——訪問者模式(Vistor Pattern)
C#設計模式(23)——備忘錄模式(Memento Pattern)
使用設計模式的根本緣由是適應變化,提升代碼複用率,使軟件更具備可維護性和可擴展性。而且,在進行設計的時候,也須要遵循如下幾個原則:單一職責原則、開放封閉原則、里氏代替原則、依賴倒置原則、接口隔離原則、合成複用原則和迪米特法則。下面就分別介紹了每種設計原則。
就一個類而言,應該只有一個引發它變化的緣由。若是一個類承擔的職責過多,就等於把這些職責耦合在一塊兒,一個職責的變化可能會影響到其餘的職責,另外,把多個職責耦合在一塊兒,也會影響複用性。
開閉原則即OCP(Open-Closed Principle縮寫)原則,該原則強調的是:一個軟件實體(指的類、函數、模塊等)應該對擴展開放,對修改關閉。即每次發生變化時,要經過添加新的代碼來加強現有類型的行爲,而不是修改原有的代碼。
符合開閉原則的最好方式是提供一個固有的接口,而後讓全部可能發生變化的類實現該接口,讓固定的接口與相關對象進行交互。
Liskov Substitution Principle,LSP(里氏代替原則)指的是子類必須替換掉它們的父類型。也就是說,在軟件開發過程當中,子類替換父類後,程序的行爲是同樣的。只有當子類替換掉父類後,此時軟件的功能不受影響時,父類才能真正地被複用,而子類也能夠在父類的基礎上添加新的行爲。爲了就來看看違反了LSP原則的例子,具體代碼以下所示:
public class Rectangle { public virtual long Width { get; set; } public virtual long Height { get; set; } } // 正方形 public class Square : Rectangle { public override long Height { get { return base.Height; } set { base.Height = value; base.Width = value; } } public override long Width { get { return base.Width; } set { base.Width = value; base.Height = value; } } } class Test { public void Resize(Rectangle r) { while (r.Height >= r.Width) { r.Width += 1; } } var r = new Square() { Width = 10, Height = 10 }; new Test().Resize(r); }
上面的設計,正如上面註釋的同樣,在執行SmartTest的resize方法時,若是傳入的是長方形對象,當高度大於寬度時,會自動增長寬度直到超出高度。可是若是傳入的是正方形對象,則會陷入死循環。此時根本緣由是,矩形不能做爲正方形的父類,既然出現了問題,能夠進行重構,使它們倆都繼承於四邊形類。重構後的代碼以下所示:
// 四邊形 public abstract class Quadrangle { public virtual long Width { get; set; } public virtual long Height { get; set; } } // 矩形 public class Rectangle : Quadrangle { public override long Height { get; set; } public override long Width { get; set; } } // 正方形 public class Square : Quadrangle { public long _side; public Square(long side) { _side = side; } } class Test { public void Resize(Quadrangle r) { while (r.Height >= r.Width) { r.Width += 1; } } static void Main(string[] args) { var s = new Square(10); new Test().Resize(s); } }
依賴倒置(Dependence Inversion Principle, DIP)原則指的是抽象不該該依賴於細節,細節應該依賴於抽象,也就是提出的 「面向接口編程,而不是面向實現編程」。這樣能夠下降客戶與具體實現的耦合。
接口隔離原則(Interface Segregation Principle, ISP)指的是使用多個專門的接口比使用單一的總接口要好。也就是說不要讓一個單一的接口承擔過多的職責,而應把每一個職責分離到多個專門的接口中,進行接口分離。過於臃腫的接口是對接口的一種污染。
合成複用原則(Composite Reuse Principle, CRP)就是在一個新的對象裏面使用一些已有的對象,使之成爲新對象的一部分。新對象經過向這些對象的委派達到複用已用功能的目的。簡單地說,就是要儘可能使用合成/聚合,儘可能不要使用繼承。
要使用好合成複用原則,首先須要區分"Has—A"和「Is—A」的關係。
「Is—A」是指一個類是另外一個類的「一種」,是屬於的關係,而「Has—A」則不一樣,它表示某一個角色具備某一項責任。致使錯誤的使用繼承而不是聚合的常見的緣由是錯誤地把「Has—A」當成「Is—A」.例如:
實際上,僱員、經歷、學生描述的是一種角色,好比一我的是「經理」必然是「僱員」。在上面的設計中,一我的沒法同時擁有多個角色,是「僱員」就不能再是「學生」了,這顯然不合理,由於如今不少在職研究生,即便僱員也是學生。
上面的設計的錯誤源於把「角色」的等級結構與「人」的等級結構混淆起來了,誤把「Has—A」看成"Is—A"。具體的解決方法就是抽象出一個角色類:
迪米特法則(Law of Demeter,LoD)又叫最少知識原則(Least Knowledge Principle,LKP),指的是一個對象應當對其餘對象有儘量少的瞭解。也就是說,一個模塊或對象應儘可能少的與其餘實體之間發生相互做用,使得系統功能模塊相對獨立,這樣當一個模塊修改時,影響的模塊就會越少,擴展起來更加容易。
關於迪米特法則其餘的一些表述有:只與你直接的朋友們通訊;不要跟「陌生人」說話。
外觀模式(Facade Pattern)和中介者模式(Mediator Pattern)就使用了迪米特法則。
建立型模式就是用來建立對象的模式,抽象了實例化的過程。全部的建立型模式都有兩個共同點。第一,它們都將系統使用哪些具體類的信息封裝起來;第二,它們隱藏了這些類的實例是如何被建立和組織的。建立型模式包括單例模式、工廠方法模式、抽象工廠模式、建造者模式和原型模式。
工廠方法,抽象工廠, 建造者都須要一個額外的工廠類來負責實例化「一個對象」,而Prototype則是經過原型(一個特殊的工廠類)來克隆「易變對象」。
下面詳細介紹下它們。
單例模式指的是確保某一個類只有一個實例,並提供一個全局訪問點。解決的是實體對象個數的問題,而其餘的建造者模式都是解決new所帶來的耦合關係問題。其實現要點有:
單例模式的結構圖以下所示:
工廠方法模式指的是定義一個建立對象的工廠接口,由其子類決定要實例化的類,將實際建立工做推遲到子類中。它強調的是」單個對象「的變化。其實現要點有:
其具體的UML結構圖以下所示:
在工廠方法模式中,工廠類與具體產品類具備平行的等級結構,它們之間是一一對應關係。
抽象工廠模式指的是提供一個建立一系列相關或相互依賴對象的接口,使得客戶端能夠在沒必要指定產品的具體類型的狀況下,建立多個產品族中的產品對象,強調的是」系列對象「的變化。其實現要點有:
具體的UML結構圖以下所示:
建造者模式指的是將一個產品的內部表示與產品的構造過程分割開來,從而可使一個建造過程生成具體不一樣的內部表示的產品對象。強調的是產品的構造過程。其實現要點有:
具體的UML結構圖以下所示:
原型模式指的是經過給出一個原型對象來指明所要建立的對象類型,而後用複製的方法來建立出更多的同類型對象。其實現要點有:
具體的UML結構圖以下所示:
結構型模式,顧名思義討論的是類和對象的結構 ,主要用來處理類或對象的組合。它包括兩種類型,一是類結構型模式,指的是採用繼承機制來組合接口或實現;二是對象結構型模式,指的是經過組合對象的方式來實現新的功能。它包括適配器模式、橋接模式、裝飾者模式、組合模式、外觀模式、享元模式和代理模式。
適配器模式意在轉換接口,它可以使本來不能再一塊兒工做的兩個類一塊兒工做,因此常常用來在類庫的複用、代碼遷移等方面。例如DataAdapter類就應用了適配器模式。適配器模式包括類適配器模式和對象適配器模式,具體結構以下圖所示,左邊是類適配器模式,右邊是對象適配器模式。
橋接模式旨在將抽象化與實現化解耦,使得二者能夠獨立地變化。意思就是說,橋接模式把原來基類的實現化細節再進一步進行抽象,構造到一個實現化的結構中,而後再把原來的基類改形成一個抽象化的等級結構,這樣就能夠實現系統在多個維度的獨立變化,橋接模式的結構圖以下所示。
裝飾者模式又稱包裝(Wrapper)模式,它能夠動態地給一個對象添加一些額外的功能,裝飾者模式較繼承生成子類的方式更加靈活。雖然裝飾者模式可以動態地將職責附加到對象上,但它也會形成產生一些細小的對象,增長了系統的複雜度。具體的結構圖以下所示。
組合模式又稱爲部分—總體模式。組合模式將對象組合成樹形結構,用來表示總體與部分的關係。組合模式使得客戶端將單個對象和組合對象同等對待。如在.NET中WinForm中的控件,TextBox、Label等簡單控件繼承與Control類,同時GroupBox這樣的組合控件也是繼承於Control類。組合模式的具體結構圖以下所示。
在系統中,客戶端常常須要與多個子系統進行交互,這樣致使客戶端會隨着子系統的變化而變化,此時可使用外觀模式把客戶端與各個子系統解耦。外觀模式指的是爲子系統中的一組接口提供一個一致的門面,它提供了一個高層接口,這個接口使子系統更加容易使用。如電信的客戶專員,你可讓客戶專員來完成衝話費,修改套餐等業務,而不須要本身去與各個子系統進行交互。具體類結構圖以下所示:
在系統中,如何咱們須要重複使用某個對象時,此時若是重複地使用new操做符來建立這個對象的話,這對系統資源是一個極大的浪費,既然每次使用的都是同一個對象,爲何不能對其共享呢?這也是享元模式出現的緣由。
享元模式運用共享的技術有效地支持細粒度的對象,使其進行共享。在.NET類庫中,String類的實現就使用了享元模式,String類採用字符串駐留池的來使字符串進行共享。更多內容參考博文:http://www.cnblogs.com/artech/archive/2010/11/25/internedstring.html。享元模式的具體結構圖以下所示。
在系統開發中,有些對象因爲網絡或其餘的障礙,以致於不能直接對其訪問,此時能夠經過一個代理對象來實現對目標對象的訪問。如.NET中的調用Web服務等操做。
代理模式指的是給某一個對象提供一個代理,並由代理對象控制對原對象的訪問。具體的結構圖以下所示。
注:外觀模式、適配器模式和代理模式區別?
解答:這三個模式的相同之處是,它們都是做爲客戶端與真實被使用的類或系統之間的一箇中間層,起到讓客戶端間接調用真實類的做用,不一樣之處在於,所應用的場合和意圖不一樣。
代理模式與外觀模式主要區別在於,代理對象沒法直接訪問對象,只能由代理對象提供訪問,而外觀對象提供對各個子系統簡化訪問調用接口,而適配器模式則不須要虛構一個代理者,目的是複用原有的接口。外觀模式是定義新的接口,而適配器則是複用一個原有的接口。
另外,它們應用設計的不一樣階段,外觀模式用於設計的前期,由於系統須要前期就須要依賴於外觀,而適配器應用於設計完成以後,當發現設計完成的類沒法協同工做時,能夠採用適配器模式。然而不少狀況下在設計初期就要考慮適配器模式的使用,如涉及到大量第三方應用接口的狀況;代理模式是模式完成後,想以服務的方式提供給其餘客戶端進行調用,此時其餘客戶端可使用代理模式來對模塊進行訪問。
總之,代理模式提供與真實類一致的接口,旨在用來代理類來訪問真實的類,外觀模式旨在簡化接口,適配器模式旨在轉換接口。
行爲型模式是對在不一樣對象之間劃分責任和算法的抽象化。行爲模式不只僅關於類和對象,還關於它們之間的相互做用。行爲型模式又分爲類的行爲模式和對象的行爲模式兩種。
行爲型模式包括11種模式:模板方法模式、命令模式、迭代器模式、觀察者模式、中介者模式、狀態模式、策略模式、責任鏈模式、訪問者模式、解釋器模式和備忘錄模式。
在現實生活中,有論文模板,簡歷模板等。在現實生活中,模板的概念是給定必定的格式,而後其餘全部使用模板的人能夠根據本身的需求去實現它。一樣,模板方法也是這樣的。
模板方法模式是在一個抽象類中定義一個操做中的算法骨架,而將一些具體步驟實現延遲到子類中去實現。模板方法使得子類能夠不改變算法結構的前提下,從新定義算法的特定步驟,從而達到複用代碼的效果。具體的結構圖以下所示。
以生活中作菜爲例子實現的模板方法結構圖
命令模式屬於對象的行爲模式,命令模式把一個請求或操做封裝到一個對象中,經過對命令的抽象化來使得發出命令的責任和執行命令的責任分隔開。命令模式的實現能夠提供命令的撤銷和恢復功能。具體的結構圖以下所示。
迭代器模式是針對集合對象而生的,對於集合對象而言,必然涉及到集合元素的添加刪除操做,也確定支持遍歷集合元素的操做,此時若是把遍歷操做也放在集合對象的話,集合對象就承擔太多的責任了,此時能夠進行責任分離,把集合的遍歷放在另外一個對象中,這個對象就是迭代器對象。
迭代器模式提供了一種方法來順序訪問一個集合對象中各個元素,而又無需暴露該對象的內部表示,這樣既能夠作到不暴露集合的內部結構,又可讓外部代碼透明地訪問集合內部元素。具體的結構圖以下所示。
在現實生活中,到處可見觀察者模式,例如,微信中的訂閱號,訂閱博客和QQ微博中關注好友,這些都屬於觀察者模式的應用。
觀察者模式定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個主題對象,這個主題對象在狀態發生變化時,會通知全部觀察者對象,使它們可以自動更新本身的行爲。具體結構圖以下所示:
在現實生活中,有不少中介者模式的身影,例如QQ遊戲平臺,聊天室、QQ羣和短信平臺,這些都是中介者模式在現實生活中的應用。
中介者模式,定義了一箇中介對象來封裝一系列對象之間的交互關係。中介者使各個對象之間不須要顯式地相互引用,從而使耦合性下降,並且能夠獨立地改變它們之間的交互行爲。具體的結構圖以下所示:
每一個對象都有其對應的狀態,而每一個狀態又對應一些相應的行爲,若是某個對象有多個狀態時,那麼就會對應不少的行爲。那麼對這些狀態的判斷和根據狀態完成的行爲,就會致使多重條件語句,而且若是添加一種新的狀態時,須要更改以前現有的代碼。這樣的設計顯然違背了開閉原則,狀態模式正是用來解決這樣的問題的。
狀態模式——容許一個對象在其內部狀態改變時自動改變其行爲,對象看起來就像是改變了它的類。具體的結構圖以下所示:
在現實生活中,中國的所得稅,分爲企業所得稅、外商投資企業或外商企業所得稅和我的所得稅,針對於這3種所得稅,每種所計算的方式不一樣,我的所得稅有我的所得稅的計算方式,而企業所得稅有其對應計算方式。若是不採用策略模式來實現這樣一個需求的話,咱們會定義一個所得稅類,該類有一個屬性來標識所得稅的類型,而且有一個計算稅收的CalculateTax()方法,在該方法體內須要對稅收類型進行判斷,經過if-else語句來針對不一樣的稅收類型來計算其所得稅。這樣的實現確實能夠解決這個場景,可是這樣的設計不利於擴展,若是系統後期須要增長一種所得稅時,此時不得不回去修改CalculateTax方法來多添加一個判斷語句,這樣明白違背了「開放——封閉」原則。此時,咱們能夠考慮使用策略模式來解決這個問題,既然稅收方法是這個場景中的變化部分,此時天然能夠想到對稅收方法進行抽象,這也是策略模式實現的精髓所在。
策略模式是對算法的包裝,是把使用算法的責任和算法自己分割開,委派給不一樣的對象負責。策略模式一般把一系列的算法包裝到一系列的策略類裏面。用一句話慨括策略模式就是——「將每一個算法封裝到不一樣的策略類中,使得它們能夠互換」。下面是策略模式的結構圖:
在現實生活中,有不少請求並非一我的說了就算的,例如面試時的工資,低於1萬的薪水可能技術經理就能夠決定了,可是1萬~1萬5的薪水可能技術經理就沒這個權利批准,可能須要請求技術總監的批准。
責任鏈模式——某個請求須要多個對象進行處理,從而避免請求的發送者和接收之間的耦合關係。將這些對象連成一條鏈子,並沿着這條鏈子傳遞該請求,直到有對象處理它爲止。具體結構圖以下所示:
訪問者模式是封裝一些施加於某種數據結構之上的操做。一旦這些操做須要修改的話,接受這個操做的數據結構則能夠保存不變。訪問者模式適用於數據結構相對穩定的系統, 它把數據結構和做用於數據結構之上的操做之間的耦合度下降,使得操做集合能夠相對自由地改變。具體結構圖以下所示:
生活中的手機通信錄備忘錄,操做系統備份點,數據庫備份等都是備忘錄模式的應用。備忘錄模式是在不破壞封裝的前提下,捕獲一個對象的內部狀態,並在該對象以外保存這個狀態,這樣之後就能夠把該對象恢復到原先的狀態。具體的結構圖以下所示:
解釋器模式是一個比較少用的模式,因此我本身也沒有對該模式進行深刻研究,在生活中,英漢詞典的做用就是實現英文和中文互譯,這就是解釋器模式的應用。
解釋器模式是給定一種語言,定義它文法的一種表示,並定義一種解釋器,這個解釋器使用該表示來解釋器語言中的句子。具體的結構圖以下所示:
23種設計模式,其實前輩們總結出來解決問題的方式,它們追求的宗旨仍是保證系統的低耦合高內聚,指導它們的原則無非就是封裝變化,責任單一,面向接口編程等設計原則。以後,我會繼續分享本身WCF的學習過程,儘管博客園中有不少WCF系列,以前以爲不必寫,以爲會用就好了,可是不寫,總感受知識不是本身的,感受沒有深刻,因此仍是想寫這樣一個系列,但願各位博友後面多多支持。
PS: 不少論壇都看到初學者問,WCF如今還有沒有必要深刻學之類的問題,由於他們以爲這些技術可能會過期,說不定到時候微軟又推出了一個新的SOA的實現方案了,那豈不是白花時間深刻學了,因此就以爲不必深刻去學,知道用就能夠了。對於這個問題,我以前也有這樣一樣的感受,可是如今我以爲,儘管WCF技術可能會被替換,但深刻了解一門技術,重點不是知道一些更高深API的調用啊,而是瞭解它的實現機制和思惟方式,即便後面這個技術被替代了,其背後機制也確定是類似的。因此深刻了解了一個技術,你就會感受新的技術熟悉,對其感受放鬆。而且,你深刻了解完一門技術以後,你面試時也敢說你很好掌握了這門技術,而不至於說平時使用的不少,一旦深刻問時殊不知道背後實現原理。這也是我要寫WCF系列的緣由。但願這點意見對一些初學者有幫助。