Python學習路程-經常使用設計模式學習

本節內容html

 

  1. 設計模式介紹
  2. 設計模式分類
  3. 設計模式6大原則

1.設計模式介紹

設計模式(Design Patterns)python

                                  ——可複用面向對象軟件的基礎程序員

設計模式(Design pattern)是一套被反覆使用、多數人知曉的、通過分類編目的、代碼設計經驗的總結。使用設計模式是爲了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。 毫無疑問,設計模式於己於他人於系統都是多贏的,設計模式使代碼編制真正工程化,設計模式是軟件工程的基石,如同大廈的一塊塊磚石同樣。項目中合理的運用設計模式能夠完美的解決不少問題,每種模式在如今中都有相應的原理來與之對應,每個模式描述了一個在咱們周圍不斷重複發生的問題,以及該問題的核心解決方案,這也是它能被普遍應用的緣由。算法

 

2. 設計模式分類

經典的《設計模式》一書概括出23種設計模式,這23種模式又可歸爲,建立型、結構型和行爲型3大類spring

2.1.建立型模式

前面講過,社會化的分工愈來愈細,天然在軟件設計方面也是如此,所以對象的建立和對象的使用分開也就成爲了必然趨勢。由於對象的建立會消耗掉系統的不少資源,因此單獨對對象的建立進行研究,從而可以高效地建立對象就是建立型模式要探討的問題。這裏有6個具體的建立型模式可供研究,它們分別是:編程

簡單工廠模式(Simple Factory);設計模式

工廠方法模式(Factory Method);安全

抽象工廠模式(Abstract Factory);架構

建立者模式(Builder);工具

原型模式(Prototype);

單例模式(Singleton)。

說明:嚴格來講,簡單工廠模式不是GoF總結出來的23種設計模式之一。

2.2 結構型模式

在解決了對象的建立問題以後,對象的組成以及對象之間的依賴關係就成了開發人員關注的焦點,由於如何設計對象的結構、繼承和依賴關係會影響到後續程序的維護性、代碼的健壯性、耦合性等。對象結構的設計很容易體現出設計人員水平的高低,這裏有7個具體的結構型模式可供研究,它們分別是:

外觀模式(Facade);

適配器模式(Adapter);

代理模式(Proxy);

裝飾模式(Decorator);

橋模式(Bridge);

組合模式(Composite);

享元模式(Flyweight)

 

2.3 行爲型模式

在對象的結構和對象的建立問題都解決了以後,就剩下對象的行爲問題了,若是對象的行爲設計的好,那麼對象的行爲就會更清晰,它們之間的協做效率就會提升,這裏有11個具體的行爲型模式可供研究,它們分別是:

模板方法模式(Template Method);

觀察者模式(Observer);

狀態模式(State);

策略模式(Strategy);

職責鏈模式(Chain of Responsibility);

命令模式(Command);

訪問者模式(Visitor);

調停者模式(Mediator);

備忘錄模式(Memento);

迭代器模式(Iterator);

解釋器模式(Interpreter)。

 

3. 設計模式的六大原則

一、開閉原則(Open Close Principle)

開閉原則就是說對擴展開放,對修改關閉。在程序須要進行拓展的時候,不能去修改原有的代碼,實現一個熱插拔的效果。因此一句話歸納就是:爲了使程序的擴展性好,易於維護和升級。想要達到這樣的效果,咱們須要使用接口和抽象類,後面的具體設計中咱們會提到這點。

二、里氏代換原則(Liskov Substitution Principle)

里氏代換原則(Liskov Substitution Principle LSP)面向對象設計的基本原則之一。 里氏代換原則中說,任何基類能夠出現的地方,子類必定能夠出現。 LSP是繼承複用的基石,只有當衍生類能夠替換掉基類,軟件單位的功能不受到影響時,基類才能真正被複用,而衍生類也可以在基類的基礎上增長新的行爲。里氏代換原則是對「開-閉」原則的補充。實現「開-閉」原則的關鍵步驟就是抽象化。而基類與子類的繼承關係就是抽象化的具體實現,因此里氏代換原則是對實現抽象化的具體步驟的規範。—— From Baidu 百科

三、依賴倒轉原則(Dependence Inversion Principle)

這個是開閉原則的基礎,具體內容:真對接口編程,依賴於抽象而不依賴於具體。

四、接口隔離原則(Interface Segregation Principle)

這個原則的意思是:使用多個隔離的接口,比使用單個接口要好。仍是一個下降類之間的耦合度的意思,從這兒咱們看出,其實設計模式就是一個軟件的設計思想,從大型軟件架構出發,爲了升級和維護方便。因此上文中屢次出現:下降依賴,下降耦合。

五、迪米特法則(最少知道原則)(Demeter Principle)

爲何叫最少知道原則,就是說:一個實體應當儘可能少的與其餘實體之間發生相互做用,使得系統功能模塊相對獨立。

六、合成複用原則(Composite Reuse Principle)

原則是儘可能使用合成/聚合的方式,而不是使用繼承。

 

工廠模式

工廠模式(Factory Pattern)是 Java 中最經常使用的設計模式之一。這種類型的設計模式屬於建立型模式,它提供了一種建立對象的最佳方式。

在工廠模式中,咱們在建立對象時不會對客戶端暴露建立邏輯,而且是經過使用一個共同的接口來指向新建立的對象。

 

意圖:

定義一個用於建立對象的接口,讓子類決定實例化哪個類。Factory Method 使個類的實例化延遲到其子類。 

適用性:

當一個類不知道它所必須建立的對象的類的時候。

當一個類但願由它的子類來指定它所建立的對象的時候。

當類將建立對象的職責委託給多個幫助子類中的某一個,而且你但願將哪個幫助子類是代理者這一信息局部化的時候。

簡單工廠模式

優勢:客戶端不須要修改代碼。
缺點: 當須要增長新的運算類的時候,不只需新加運算類,還要修改工廠類,違反了開閉原則。  

工廠方法模式

這個和簡單工廠有區別,簡單工廠模式只有一個工廠,工廠方法模式對每個產品都有相應的工廠

  好處:增長一個運算類(例如N次方類),只須要增長運算類和相對應的工廠,兩個類,不須要修改工廠類。

  缺點:增長運算類,會修改客戶端代碼,工廠方法只是把簡單工廠的內部邏輯判斷移到了客戶端進行。

  

抽象工廠模式

每個模式都是針對必定問題的解決方案。抽象工廠模式與工廠方法模式的最大區別就在於,工廠方法模式針對的是一個產品等級結構;而抽象工廠模式則須要面對多個產品等級結構。

  在學習抽象工廠具體實例以前,應該明白兩個重要的概念:產品族和產品等級。

所謂產品族,是指位於不一樣產品等級結構中,功能相關聯的產品組成的家族。好比AMD的主板、芯片組、CPU組成一個家族,Intel的主板、芯片組、CPU組成一個家族。而這兩個家族都來自於三個產品等級:主板、芯片組、CPU。一個等級結構是由相同的結構的產品組成,示意圖以下:

顯然,每個產品族中含有產品的數目,與產品等級結構的數目是相等的。產品的等級結構與產品族將產品按照不一樣方向劃分,造成一個二維的座標系。橫軸表示產品的等級結構,縱軸表示產品族,上圖共有兩個產品族,分佈於三個不一樣的產品等級結構中。只要指明一個產品所處的產品族以及它所屬的等級結構,就能夠惟一的肯定這個產品。

  上面所給出的三個不一樣的等級結構具備平行的結構。所以,若是採用工廠方法模式,就勢必要使用三個獨立的工廠等級結構來對付這三個產品等級結構。因爲這三個產品等級結構的類似性,會致使三個平行的工廠等級結構。隨着產品等級結構的數目的增長,工廠方法模式所給出的工廠等級結構的數目也會隨之增長。以下圖:

那麼,是否可使用同一個工廠等級結構來對付這些相同或者極爲類似的產品等級結構呢?固然能夠的,並且這就是抽象工廠模式的好處。同一個工廠等級結構負責三個不一樣產品等級結構中的產品對象的建立。

 

 能夠看出,一個工廠等級結構能夠建立出分屬於不一樣產品等級結構的一個產品族中的全部對象。顯然,這時候抽象工廠模式比簡單工廠模式、工廠方法模式更有效率。對應於每個產品族都有一個具體工廠。而每個具體工廠負責建立屬於同一個產品族,可是分屬於不一樣等級結構的產品。

 

抽象工廠模式結構

  抽象工廠模式是對象的建立模式,它是工廠方法模式的進一步推廣。

  假設一個子系統須要一些產品對象,而這些產品又屬於一個以上的產品等級結構。那麼爲了將消費這些產品對象的責任和建立這些產品對象的責任分割開來,能夠引進抽象工廠模式。這樣的話,消費產品的一方不須要直接參與產品的建立工做,而只須要向一個公用的工廠接口請求所須要的產品。

  經過使用抽象工廠模式,能夠處理具備相同(或者類似)等級結構中的多個產品族中的產品對象的建立問題。以下圖所示:

 

  因爲這兩個產品族的等級結構相同,所以使用同一個工廠族也能夠處理這兩個產品族的建立問題,這就是抽象工廠模式。

  根據產品角色的結構圖,就不難給出工廠角色的結構設計圖。

 

能夠看出,每個工廠角色都有兩個工廠方法,分別負責建立分屬不一樣產品等級結構的產品對象。

 

抽象工廠的功能是爲一系列相關對象或相互依賴的對象建立一個接口。必定要注意,這個接口內的方法不是任意堆砌的,而是一系列相關或相互依賴的方法。好比上面例子中的主板和CPU,都是爲了組裝一臺電腦的相關對象。不一樣的裝機方案,表明一種具體的電腦系列。

因爲抽象工廠定義的一系列對象一般是相關或相互依賴的,這些產品對象就構成了一個產品族,也就是抽象工廠定義了一個產品族。

  這就帶來很是大的靈活性,切換產品族的時候,只要提供不一樣的抽象工廠實現就能夠了,也就是說如今是以一個產品族做爲一個總體被切換。

 

在什麼狀況下應當使用抽象工廠模式

  1.一個系統不該當依賴於產品類實例如何被建立、組合和表達的細節,這對於全部形態的工廠模式都是重要的。

  2.這個系統的產品有多於一個的產品族,而系統只消費其中某一族的產品。

  3.同屬於同一個產品族的產品是在一塊兒使用的,這一約束必須在系統的設計中體現出來。(好比:Intel主板必須使用Intel CPU、Intel芯片組)

  4.系統提供一個產品類的庫,全部的產品以一樣的接口出現,從而使客戶端不依賴於實現。

抽象工廠模式的起源

  抽象工廠模式的起源或者最先的應用,是用於建立分屬於不一樣操做系統的視窗構建。好比:命令按鍵(Button)與文字框(Text)都是視窗構建,在UNIX操做系統的視窗環境和Windows操做系統的視窗環境中,這兩個構建有不一樣的本地實現,它們的細節有所不一樣。

  在每個操做系統中,都有一個視窗構建組成的構建家族。在這裏就是Button和Text組成的產品族。而每個視窗構件都構成本身的等級結構,由一個抽象角色給出抽象的功能描述,而由具體子類給出不一樣操做系統下的具體實現。

 

抽象工廠模式的優勢

  • 分離接口和實現

  客戶端使用抽象工廠來建立須要的對象,而客戶端根本就不知道具體的實現是誰,客戶端只是面向產品的接口編程而已。也就是說,客戶端從具體的產品實現中解耦。

  • 使切換產品族變得容易

  由於一個具體的工廠實現表明的是一個產品族,好比上面例子的從Intel系列到AMD系列只須要切換一下具體工廠。

抽象工廠模式的缺點

  • 不太容易擴展新的產品

  若是須要給整個產品族添加一個新的產品,那麼就須要修改抽象工廠,這樣就會致使修改全部的工廠實現類。

 

  

建造者模式

意圖:

將一個複雜對象的構建與它的表示分離,使得一樣的構建過程能夠建立不一樣的表示。

適用性:

當建立複雜對象的算法應該獨立於該對象的組成部分以及它們的裝配方式時。

當構造過程必須容許被構造的對象有不一樣的表示時。

 

 

  

 單例模式

意圖: 

保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。

適用性:

當類只能有一個實例並且客戶能夠從一個衆所周知的訪問點訪問它時。

當這個惟一實例應該是經過子類化可擴展的,而且客戶應該無需更改代碼就能使用一個擴展的實例時。

 

  

適配器模式 

意圖

將一個類的接口轉換成客戶但願的另一個接口。Adapter 模式使得本來因爲接口不兼容而不能一塊兒工做的那些類能夠一塊兒工做。 
適用性:

你想使用一個已經存在的類,而它的接口不符合你的需求。

你想建立一個能夠複用的類,該類能夠與其餘不相關的類或不可預見的類(即那些接口可能不必定兼容的類)協同工做

 

  

橋接模式

參考:http://www.cnblogs.com/houleixx/archive/2008/02/23/1078877.html


生活中的一個例子:
就拿汽車在路上行駛的來講。即有小汽車又有公共汽車,它們都不但能在市區中的公路上行駛,也能在高速公路上行駛。這你會發現,對於交通工具(汽車)有不一樣的類型,然而它們所行駛的環境(路)也在變化,在軟件系統中就要適應兩個方面的變化?怎樣實現才能應對這種變化呢?
概述:
在軟件系統中,某些類型因爲自身的邏輯,它具備兩個或多個維度的變化,那麼如何應對這種「多維度的變化」?如何利用面嚮對象的技術來使得該類型可以輕鬆的沿着多個方向進行變化,而又不引入額外的複雜度?這就要使用Bridge模式。
意圖:
   將抽象部分與實現部分分離,使它們均可以獨立的變化。
——《設計模式》GOF

效果及實現要點:
1.Bridge模式使用「對象間的組合關係」解耦了抽象和實現之間固有的綁定關係,使得抽象和實現能夠沿着各自的維度來變化。
2.所謂抽象和實現沿着各自維度的變化,即「子類化」它們,獲得各個子類以後,即可以任意它們,從而得到不一樣路上的不一樣汽車。
3.Bridge模式有時候相似於多繼承方案,可是多繼承方案每每違背了類的單一職責原則(即一個類只有一個變化的緣由),複用性比較差。Bridge模式是比多繼承方案更好的解決方法。
4.Bridge模式的應用通常在「兩個很是強的變化維度」,有時候即便有兩個變化的維度,可是某個方向的變化維度並不劇烈——換言之兩個變化不會致使縱橫交錯的結果,並不必定要使用Bridge模式。

適用性:
在如下的狀況下應當使用橋樑模式:
1.若是一個系統須要在構件的抽象化角色和具體化角色之間增長更多的靈活性,避免在兩個層次之間創建靜態的聯繫。
2.設計要求實現化角色的任何改變不該當影響客戶端,或者說實現化角色的改變對客戶端是徹底透明的。
3.一個構件有多於一個的抽象化角色和實現化角色,系統須要它們之間進行動態耦合。
4.雖然在系統中使用繼承是沒有問題的,可是因爲抽象化角色和具體化角色須要獨立變化,設計要求須要獨立管理這二者。
總結:
Bridge模式是一個很是有用的模式,也很是複雜,它很好的符合了開放-封閉原則和優先使用對象,而不是繼承這兩個面向對象原則

 

應用設計模式:
       橋接模式(Bridge)來作(多維度變化);
       結合上面的例子,增長一個維度"人",不一樣的人開着不一樣的汽車在不一樣的路上行駛(三個維度);
       結合上面增長一個類"人",並從新調用.
代碼實現:

  

組合模式

意圖:

將對象組合成樹形結構以表示「部分-總體」的層次結構。C o m p o s i t e 使得用戶對單個對象和組合對象的使用具備一致性。 
適用性:

你想表示對象的部分-總體層次結構。

你但願用戶忽略組合對象與單個對象的不一樣,用戶將統一地使用組合結構中的全部對象。

 

  

外觀模式

意圖:

爲子系統中的一組接口提供一個一致的界面,Facade模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。

適用性:

當你要爲一個複雜子系統提供一個簡單接口時。子系統每每由於不斷演化而變得愈來愈複雜。大多數模式使用時都會產生更多更小的類。這使得子系統更具可重用性,也更容易對子系統進行定製,但這也給那些不須要定製子系統的用戶帶來一些使用上的困難。Facade 能夠提供一個簡單的缺省視圖,這一視圖對大多數用戶來講已經足夠,而那些須要更多的可定製性的用戶能夠越過facade層。

客戶程序與抽象類的實現部分之間存在着很大的依賴性。引入facade 將這個子系統與客戶以及其餘的子系統分離,能夠提升子系統的獨立性和可移植性。

當你須要構建一個層次結構的子系統時,使用facade模式定義子系統中每層的入口點。若是子系統之間是相互依賴的,你可讓它們僅經過facade進行通信,從而簡化了它們之間的依賴關係。  

 

  

享元模式

意圖:

運用共享技術有效地支持大量細粒度的對象。

適用性:

一個應用程序使用了大量的對象。

徹底因爲使用大量的對象,形成很大的存儲開銷。

對象的大多數狀態均可變爲外部狀態。

若是刪除對象的外部狀態,那麼能夠用相對較少的共享對象取代不少組對象。

應用程序不依賴於對象標識。因爲Flyweight 對象能夠被共享,對於概念上明顯有別的對象,標識測試將返回真值。

  

代理模式

意圖:爲其餘對象提供一種代理以控制對這個對象的訪問。

主要解決:在直接訪問對象時帶來的問題,好比說:要訪問的對象在遠程的機器上。在面向對象系統中,有些對象因爲某些緣由(好比對象建立開銷很大,或者某些操做須要安全控制,或者須要進程外的訪問),直接訪問會給使用者或者系統結構帶來不少麻煩,咱們能夠在訪問此對象時加上一個對此對象的訪問層。

什麼時候使用:想在訪問一個類時作一些控制。

如何解決:增長中間層。

關鍵代碼:實現與被代理類組合。

應用實例: 一、Windows 裏面的快捷方式。 二、豬八戒去找高翠蘭結果是孫悟空變的,能夠這樣理解:把高翠蘭的外貌抽象出來,高翠蘭本人和孫悟空都實現了這個接口,豬八戒訪問高翠蘭的時候看不出來這個是孫悟空,因此說孫悟空是高翠蘭代理類。 三、買火車票不必定在火車站買,也能夠去代售點。 四、一張支票或銀行存單是帳戶中資金的代理。支票在市場交易中用來代替現金,並提供對簽發人帳號上資金的控制。 五、spring aop。

優勢: 一、職責清晰。 二、高擴展性。 三、智能化。

缺點: 一、因爲在客戶端和真實主題之間增長了代理對象,所以有些類型的代理模式可能會形成請求的處理速度變慢。 二、實現代理模式須要額外的工做,有些代理模式的實現很是複雜。

使用場景:按職責來劃分,一般有如下使用場景: 一、遠程代理。 二、虛擬代理。 三、Copy-on-Write 代理。 四、保護(Protect or Access)代理。 五、Cache代理。 六、防火牆(Firewall)代理。 七、同步化(Synchronization)代理。 八、智能引用(Smart Reference)代理。

 

 

 

模板方法模式

在模板模式(Template Pattern)中,一個抽象類公開定義了執行它的方法的方式/模板。它的子類能夠按須要重寫方法實現,但調用將以抽象類中定義的方式進行。這種類型的設計模式屬於行爲型模式。

意圖:定義一個操做中的算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類能夠不改變一個算法的結構便可重定義該算法的某些特定步驟。

主要解決:一些方法通用,卻在每個子類都從新寫了這一方法。

什麼時候使用:有一些通用的方法。

如何解決:將這些通用算法抽象出來。

關鍵代碼:在抽象類實現,其餘步驟在子類實現。

應用實例: 一、在造房子的時候,地基、走線、水管都同樣,只有在建築的後期纔有加壁櫥加柵欄等差別。 二、西遊記裏面菩薩定好的 81 難,這就是一個頂層的邏輯骨架。 三、Spirng 中對 Hibernate 的支持,將一些已經定好的方法封裝起來,好比開啓事務、獲取 Session、關閉 Session 等,程序員不重複寫那些已經規範好的代碼,直接丟一個實體就能夠保存。

優勢: 一、封裝不變部分,擴展可變部分。 二、提取公共代碼,便於維護。 三、行爲由父類控制,子類實現。

缺點:每個不一樣的實現都須要一個子類來實現,致使類的個數增長,使得系統更加龐大。

使用場景: 一、有多個子類共有的方法,且邏輯相同。 二、重要的、複雜的方法,能夠考慮做爲模板方法。

  

責任鏈模式

意圖:

使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係。將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有一個對象處理它爲止。

適用性:

有多個的對象能夠處理一個請求,哪一個對象處理該請求運行時刻自動肯定。

你想在不明確指定接收者的狀況下,向多個對象中的一個提交一個請求。

可處理一個請求的對象集合應被動態指定。

 

 

 

觀察者模式

意圖:

定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時, 全部依賴於它的對象都獲得通知並被自動更新。

適用性:

當一個抽象模型有兩個方面, 其中一個方面依賴於另外一方面。將這兩者封裝在獨立的對象中以使它們能夠各自獨立地改變和複用。

當對一個對象的改變須要同時改變其它對象, 而不知道具體有多少對象有待改變。

當一個對象必須通知其它對象,而它又不能假定其它對象是誰。換言之, 你不但願這些對象是緊密耦合的。

 

 

 

  

策略模式

意圖:定義一系列的算法,把它們一個個封裝起來, 而且使它們可相互替換。

主要解決:在有多種算法類似的狀況下,使用 if...else 所帶來的複雜和難以維護。

什麼時候使用:一個系統有許多許多類,而區分它們的只是他們直接的行爲。

如何解決:將這些算法封裝成一個一個的類,任意地替換。

關鍵代碼:實現同一個接口。

應用實例: 一、諸葛亮的錦囊妙計,每個錦囊就是一個策略。 二、旅行的出遊方式,選擇騎自行車、坐汽車,每一種旅行方式都是一個策略。 三、JAVA AWT 中的 LayoutManager。

優勢: 一、算法能夠自由切換。 二、避免使用多重條件判斷。 三、擴展性良好。

缺點: 一、策略類會增多。 二、全部策略類都須要對外暴露。

使用場景: 一、若是在一個系統裏面有許多類,它們之間的區別僅在於它們的行爲,那麼使用策略模式能夠動態地讓一個對象在許多行爲中選擇一種行爲。 二、一個系統須要動態地在幾種算法中選擇一種。 三、若是一個對象有不少的行爲,若是不用恰當的模式,這些行爲就只好使用多重的條件選擇語句來實現。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#_*_coding:utf-8_*_
__author__  =  'Alex Li'
 
 
 
class  TravelStrategy( object ):
     '''
     出行策略
     '''
 
     def  travelAlgorithm( self ):
         pass
 
class  AirplaneStrategy(TravelStrategy):
     def  travelAlgorithm( self ):
         print ( "坐飛機出行...." )
 
class  TrainStrategy(TravelStrategy):
     def  travelAlgorithm( self ):
         print ( "坐高鐵出行...." )
 
 
class  CarStrategy(TravelStrategy):
     def  travelAlgorithm( self ):
         print ( "自駕出行...." )
 
class  BicycleStrategy(TravelStrategy):
     def  travelAlgorithm( self ):
         print ( "騎車出行...." )
 
 
class  TravelInterface( object ):
     def  __init__( self ,travel_strategy):
         self .travel_strategy  =  travel_strategy
 
     def  set_strategy( self ,travel_strategy):
         self .travel_strategy  =  travel_strategy
     def  travel( self ):
         return  self .travel_strategy.travelAlgorithm()
 
 
 
#坐飛機
travel  =  TravelInterface(AirplaneStrategy())
 
travel.travel()
 
#改開車
travel.set_strategy(TrainStrategy())
travel.travel()
相關文章
相關標籤/搜索