Java常見設計模式總結

2021-09-20 13:00:32

 一、設計模式總述:

1、什麼是設計模式:

        設計模式是一套經過反覆使用的程式碼設計經驗,目的是為了重用程式碼、讓程式碼更容易被他人理解、保證程式碼可靠性。 設計模式於己於人於系統都是多贏的,它使得程式碼編寫真正工程化,它是軟體工程的基石,如同大廈的一塊塊磚石一樣。專案中合理的運用設計模式可以完美的解決很多問題,每種模式在現實中都有相應的原理來與之對應,每種模式描述了一個在我們周圍不斷重複發生的問題,以及該問題的核心解決方案,這也是它能被廣泛應用的原因。總體來說,設計模式分為三大類:

  • 建立型模式:共5種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式
  • 結構型模式:共7種:介面卡模式、裝飾器模式、代理模式、橋接模式、外觀模式、組合模式、享元模式
  • 行為型模式:共11種:策略模式、模板方法模式、觀察者模式、責任鏈模式、存取者模式、中介者模式、迭代器模式、命令模式、狀態模式、備忘錄模式、直譯器模式

其實還有兩類:並行型模式和執行緒池模式,用一個圖片來整體描述一下:

2、設計模式的六大原則:

(1)開閉原則 (Open Close Principle) :

        開閉原則指的是對擴充套件開放,對修改關閉。在對程式進行擴充套件的時候,不能去修改原有的程式碼,想要達到這樣的效果,我們就需要使用介面或者抽象類

(2)依賴倒轉原則 (Dependence Inversion Principle):

        依賴倒置原則是開閉原則的基礎,指的是針對介面程式設計,依賴於抽象而不依賴於具體

(3)里氏替換原則 (Liskov Substitution Principle) :

        里氏替換原則是繼承與複用的基石,只有當子類可以替換掉基礎類別,且系統的功能不受影響時,基礎類別才能被複用,而子類也能夠在基礎類上增加新的行為。所以里氏替換原則指的是任何基礎類別可以出現的地方,子類一定可以出現。

        里氏替換原則是對 「開閉原則」 的補充,實現 「開閉原則」 的關鍵步驟就是抽象化,而基礎類別與子類的繼承關係就是抽象化的具體實現,所以里氏替換原則是對實現抽象化的具體步驟的規範。

(4)介面隔離原則 (Interface Segregation Principle):

        使用多個隔離的介面,比使用單個介面要好,降低介面之間的耦合度與依賴,方便升級和維護方便

(5)迪米特原則 (Demeter Principle):

        迪米特原則,也叫最少知道原則,指的是一個類應當儘量減少與其他實體進行相互作用,使得系統功能模組相對獨立,降低耦合關係。該原則的初衷是降低類的耦合,雖然可以避免與非直接的類通訊,但是要通訊,就必然會通過一個「中介」來發生關係,過分的使用迪米特原則,會產生大量的中介和傳遞類,導致系統複雜度變大,所以採用迪米特法則時要反覆權衡,既要做到結構清晰,又要高內聚低耦合。

(6)合成複用原則 (Composite Reuse Principle):

        儘量使用組合/聚合的方式,而不是使用繼承。

二、Java的23種設計模式:

        接下來我們詳細介紹Java中23種設計模式的概念,應用場景等情況,並結合他們的特點及設計模式的原則進行分析

1、建立型-工廠方法模式:

工廠方法模式分為三種:

(1)簡單工廠模式:

建立一個工廠類,並定義一個介面對實現了同一介面的產品類進行建立。首先看下關係圖:

(2)工廠方法模式:

工廠方法模式是對簡單工廠模式的改進,簡單工廠的缺陷在於不符合「開閉原則」,每次新增新產品類就需要修改工廠類,不利於系統的擴充套件維護。而工廠方法將工廠抽象化,並定義一個建立物件的介面。每增加新產品,只需增加該產品以及對應的具體實現工廠類,由具體工廠類決定要範例化的產品是哪個,將物件的建立與範例化延遲到子類,這樣工廠的設計就符合「開閉原則」了,擴充套件時不必去修改原來的程式碼。UML關係圖如下:

 (3)靜態工廠方法模式:

靜態工廠模式是將工廠方法模式裡的方法置為靜態的,不需要建立範例,直接呼叫即可。

工廠方法模式詳情文章:Java設計模式之建立型:工廠模式詳解(簡單工廠+工廠方法+抽象工廠)

2、建立型-抽象工廠模式:

        抽象工廠模式主要用於建立相關物件的家族。當一個產品族中需要被設計在一起工作時,通過抽象工廠模式,能夠保證使用者端始終只使用同一個產品族中的物件;並且通過隔離具體類的生成,使得使用者端不需要明確指定具體生成類;所有的具體工廠都實現了抽象工廠中定義的公共介面,因此只需要改變具體工廠的範例,就可以在某種程度上改變整個軟體系統的行為。

        但該模式的缺點在於新增新的行為時比較麻煩,如果需要新增一個新產品族物件時,需要更改介面及其下所有子類,這必然會帶來很大的麻煩。

        UML結構圖如下:

抽象工廠模式詳情:Java設計模式之建立型:工廠模式詳解(簡單工廠+工廠方法+抽象工廠)

3、建立型-建造者模式:

         建造者模式將複雜產品的建立步驟分解在在不同的方法中,使得建立過程更加清晰,從而更精確控制複雜物件的產生過程;通過隔離複雜物件的構建與使用,也就是將產品的建立與產品本身分離開來,使得同樣的構建過程可以建立不同的物件;並且每個具體建造者都相互獨立,因此可以很方便地替換具體建造者或增加新的具體建造者,使用者使用不同的具體建造者即可得到不同的產品物件。UML結構圖如下:

 建造者模式詳情:Java設計模式之建立型:建造者模式

4、建立型-單例模式:

        單例模式可以確保系統中某個類只有一個範例,該類自行範例化並向整個系統提供這個範例的公共存取點,除了該公共存取點,不能通過其他途徑存取該範例。單例模式的優點在於:

  • 系統中只存在一個共用的範例物件,無需頻繁建立和銷燬物件,節約了系統資源,提高系統的效能
  • 可以嚴格控制客戶怎麼樣以及何時存取單例物件。

單例模式的寫法有好幾種,主要有三種:懶漢式單例、餓漢式單例、登記式單例。

單例模式詳情:Java設計模式之建立型:單例模式

5、建立型-原型模式:

        原型模式也是用於物件的建立,通過將一個物件作為原型,對其進行復制克隆,產生一個與源物件類似的新物件。UML類圖如下:

 在 Java 中,原型模式的核心是就是原型類 Prototype,Prototype 類需要具備以下兩個條件:

  • 實現 Cloneable 介面:
  • 重寫 Object 類中的 clone() 方法,用於返回物件的拷貝;

Object 類中的 clone() 方法預設是淺拷貝,如果想要深拷貝物件,則需要在 clone() 方法中自定義自己的複製邏輯。

  • 淺複製:將一個物件複製後,基本資料型別的變數會重新建立,而參照型別指向的還是原物件所指向的記憶體地址。
  • 深複製:將一個物件複製後,不論是基本資料型別還有參照型別,都是重新建立的。

        使用原型模式進行建立物件不僅簡化物件的建立步驟,還比 new 方式建立物件的效能要好的多,因為 Object 類的 clone() 方法是一個本地方法,直接操作記憶體中的二進位制流,特別是複製大物件時,效能的差別非常明顯;

原型模式詳情:Java設計模式之建立型:原型模式

        

        上面我們介紹了5種建立型模式,下面我們就開始介紹下7種結構型模式:介面卡模式、裝飾模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。其中物件的介面卡模式是各種模式的起源,如下圖:

6、結構型-介面卡模式:

        介面卡模式主要用於將一個類或者介面轉化成使用者端希望的格式,使得原本不相容的類可以在一起工作,將目標類和適配者類解耦;同時也符合「開閉原則」,可以在不修改原始碼的基礎上增加新的介面卡類;將具體的實現封裝在適配者類中,對於使用者端類來說是透明的,而且提高了適配者的複用性,但是缺點在於更換介面卡的實現過程比較複雜。

        所以,介面卡模式比較適合以下場景:

  • (1)系統需要使用現有的類,而這些類的介面不符合系統的介面。
  • (2)使用第三方元件,元件介面定義和自己定義的不同,不希望修改自己的介面,但是要使用第三方元件介面的功能。

下面有個非常形象的例子很好地說明了什麼是介面卡模式:

介面卡模式的主要實現有三種:類的介面卡模式、物件的介面卡模式、介面的介面卡模式。三者的使用場景如下:

  • 類的介面卡模式:當希望將一個類轉換成滿足另一個新介面的類時,可以使用類的介面卡模式,建立一個新類,繼承原有的類,實現新的介面即可。
  • 物件的介面卡模式:當希望將一個物件轉換成滿足另一個新介面的物件時,可以建立一個Wrapper類,持有原類的一個範例,在Wrapper類的方法中,呼叫範例的方法就行。
  • 介面的介面卡模式:當不希望實現一個介面中所有的方法時,可以建立一個抽象類Wrapper,實現所有方法,我們寫別的類的時候,繼承抽象類即可。

介面卡模式詳情:Java設計模式之結構型:介面卡模式

7、結構型-裝飾器模式:

        裝飾器模式可以動態給物件新增一些額外的職責從而實現功能的拓展,在執行時選擇不同的裝飾器,從而實現不同的行為;比使用繼承更加靈活,通過對不同的裝飾類進行排列組合,創造出很多不同行為,得到功能更為強大的物件;符合「開閉原則」,被裝飾類與裝飾類獨立變化,使用者可以根據需要增加新的裝飾類和被裝飾類,在使用時再對其進行組合,原有程式碼無須改變。裝飾器模式的UML結構圖如下:

        但是裝飾器模式也存在缺點,首先會產生很多的小物件,增加了系統的複雜性,第二是排錯比較困難,對於多次裝飾的物件,偵錯時尋找錯誤可能需要逐級排查,較為煩瑣。

裝飾器模式詳情:Java設計模式之結構型:裝飾器模式

8、結構型-代理模式:

        代理模式的設計動機是通過代理物件來存取真實物件,通過建立一個物件代理類,由代理物件控制原物件的參照,從而實現對真實物件的操作。在代理模式中,代理物件主要起到一箇中介的作用,用於協調與連線呼叫者(即使用者端)和被呼叫者(即目標物件),在一定程度上降低了系統的耦合度,同時也保護了目標物件。但缺點是在呼叫者與被呼叫者之間增加了代理物件,可能會造成請求的處理速度變慢。UML結構圖如下:

代理模式詳情:Java設計模式之結構型:代理模式

9、結構型-橋接模式:

        橋接模式將系統的抽象部分與實現部分分離解耦,使他們可以獨立的變化。為了達到讓抽象部分和實現部分獨立變化的目的,橋接模式使用組合關係來代替繼承關係,抽象部分擁有實現部分的介面物件,從而能夠通過這個介面物件來呼叫具體實現部分的功能。也就是說,橋接模式中的橋接是一個單方向的關係,只能夠抽象部分去使用實現部分的物件,而不能反過來。 

        橋接模式符合「開閉原則」,提高了系統的可拓展性,在兩個變化維度中任意擴充套件一個維度,都不需要修改原來的系統;並且實現細節對客戶不透明,可以隱藏實現細節。但是由於聚合關係建立在抽象層,要求開發者針對抽象進行程式設計,這增加系統的理解和設計難度。橋接模式的UML結構圖如下:

        就像在Java中我們使用 JDBC 連線資料庫時,在各個資料庫之間進行切換,基本不需要動太多的程式碼,原因就是使用了橋接模式,JDBC 提供統一介面,每個資料庫提供各自的實現,然後由橋接類建立一個連線資料庫的驅動,使用某一個資料庫的時候只需要切換一下就行。JDBC 的結構圖如下:

         在 JDBC 中,橋接模式的實現化角色 (Implementor) 為的 Driver 介面,具體實現化 (Concrete Implementor) 角色對應 MysqlDriver、OracleDriver 和 MariadbDriver,擴充套件抽象化 (Refined Abstraction) 角色對應 DriverManager,不具有抽象化 (Abstraction) 角色作為擴充套件抽象化角色的父類別。

橋接模式詳情:Java設計模式之結構型:橋接模式

10、結構型-外觀模式:

        外觀模式通過對使用者端提供一個統一的介面,用於存取子系統中的一群介面。使用外觀模式有以下幾點好處:

(1)更加易用:使得子系統更加易用,使用者端不再需要了解子系統內部的實現,也不需要跟眾多子系統內部的模組進行互動,只需要跟外觀類互動就可以了;

(2)鬆散耦合:將使用者端與子系統解耦,讓子系統內部的模組能更容易擴充套件和維護。

(3)更好的劃分存取層次:通過合理使用 Facade,可以更好地劃分存取的層次,有些方法是對系統外的,有些方法是系統內部使用的。把需要暴露給外部的功能集中到門面中,這樣既方便使用者端使用,也很好地隱藏了內部的細節。

        但是如果外觀模式對子系統類做太多的限制則減少了可變性和靈活性,所以外觀模式適用於為複雜子系統提供一個簡單介面,提高系統的易用性場景 以及 引入外觀模式將子系統與使用者端進行解耦,提高子系統的獨立性和可移植性。

        外觀模式的UML結構圖如下:

外觀模式詳情: Java設計模式之結構型:外觀模式

11、結構型-組合模式:

        組合模式將葉子物件和容器物件進行遞迴組合,形成樹形結構以表示「部分-整體」的層次結構,使得使用者對單個物件和組合物件的使用具有一致性,能夠像處理葉子物件一樣來處理組合物件,無需進行區分,從而使使用者程式能夠與複雜元素的內部結構進行解耦。

        組合模式最關鍵的地方是葉子物件和組合物件實現了相同的抽象構建類,它既可表示葉子物件,也可表示容器物件,客戶僅僅需要針對這個抽象構建類進行程式設計,這就是組合模式能夠將葉子節點和物件節點進行一致處理的原因。組合模式的UML結構圖如下:

組合模式詳情: Java設計模式之結構型:組合模式

12、結構型-享元模式:

        享元模式通過共用技術有效地支援細粒度、狀態變化小的物件複用,當系統中存在有多個相同的物件,那麼只共用一份,不必每個都去範例化一個物件,極大地減少系統中物件的數量,從而節省資源。

        享元模式的核心是享元工廠類,享元工廠類維護了一個物件儲存池,當用戶端需要物件時,首先從享元池中獲取,如果享元池中存在物件範例則直接返回,如果享元池中不存在,則建立一個新的享元物件範例返回給使用者,並在享元池中儲存該新增物件,這點有些單例的意思。

        工廠類通常會使用集合型別來儲存物件,如 HashMap、Hashtable、Vector 等等,在 Java 中,資料庫連線池、執行緒池等都是用享元模式的應用。

        享元模式的UML結構圖如下:

         Java 中,String 型別就是使用享元模式,String 物件是 final 型別,物件一旦建立就不可改變。而 Java 的字串常數都是存在字串常數池中的,JVM 會確保一個字串常數在常數池中只有一個拷貝。

        而且提到共用池,我們也很容易聯想到 Java 裡面的JDBC連線池,通過連線池的管理,實現了資料庫連線的共用,不需要每一次都重新建立連線,節省了資料庫重新建立的開銷,提升了系統的效能!

享元模式詳情:Java設計模式之結構型:享元模式

        前面我們介紹了7種結構型設計模式,接下來我們介紹一下11種行為型設計模式:策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、存取者模式、中介者模式、直譯器模式。先來張圖,看看這11中模式的關係:

 13、行為型-策略模式:

        將類中經常改變或者可能改變的部分提取為作為一個抽象策略介面類,然後在類中包含這個物件的範例,這樣類範例在執行時就可以隨意呼叫實現了這個介面的類的行為。

        比如定義一系列的演演算法,把每一個演演算法封裝起來,並且使它們可相互替換,使得演演算法可獨立於使用它的客戶而變化,這就是策略模式。UML結構圖如下:

        策略模式的優點在於可以動態改變物件的行為;但缺點是會產生很多策略類,並且策略模式的決定權在使用者,系統只是提供不同演演算法的實現,所以使用者端必須知道所有的策略類,並自行決定使用哪一個策略類; 

        策略模式適用用於以下幾種場景:

  • (1)應用程式需要實現特定的功能服務,而該程式有多種實現方式使用,所以需要動態地在幾種演演算法中選擇一種
  • (2)一個類定義了多種行為演演算法,並且這些行為在類的操作中以多個條件語句的形式出現,就可以將相關的條件分支移入它們各自的Strategy類中以代替這些條件語句。

策略模式詳情:Java設計模式之行為型:策略模式

14、行為型-模板方法:

        模板方法是基於繼承實現的,在抽象父類別中宣告一個模板方法,並在模板方法中定義演演算法的執行步驟(即演演算法骨架)。在模板方法模式中,可以將子類共性的部分放在父類別中實現,而特性的部分延遲到子類中實現,只需將特性部分在父類別中宣告成抽象方法即可,使得子類可以在不改變演演算法結構的情況下,重新定義演演算法中的某些步驟,不同的子類可以以不同的方式來實現這些邏輯。

        模板方法模式的優點在於符合「開閉原則」,也能夠實現程式碼複用,將不變的行為轉移到父類別,去除子類中的重複程式碼。但是缺點是不同的實現都需要定義一個子類,導致類的個數的增加使得系統更加龐大,設計更加抽象。模板方法模式的UML圖如下:

模板方法詳情:Java設計模式之行為型:模板方法模式

15、行為型-責任鏈模式:

        職責鏈可以將請求的處理者組織成一條鏈,並將請求沿著鏈傳遞,如果某個處理者能夠處理請求則處理,否則將該請求交由上級處理。使用者端只需將請求傳送到職責鏈上,無須關注請求的處理細節,通過職責鏈將請求的傳送者和處理者解耦了,這也是職責鏈的設計動機。        

       職責鏈模式可以簡化物件間的相互連線,因為使用者端和處理者都沒有對方明確的資訊,同時處理者也不知道職責鏈中的結構,處理者只需儲存一個指向後續者的參照,而不需要儲存所有候選者的參照。

        另外職責鏈模式增加了系統的靈活性,我們可以任意增加或更改處理者,甚至更改處理者的順序,不過有可能會導致一個請求無論如何也得不到處理,因為它可能被放置在鏈末端。

所以責任鏈模式有以下幾個優點:

  • (1)降低耦合度,將請求的傳送者和接收者解耦。反映在程式碼上就是不需要在類中寫很多醜陋的 if….else 語句,如果用了職責鏈,相當於我們面對一個黑箱,只需將請求遞交給其中一個處理者,然後讓黑箱內部去負責傳遞就可以了。
  • (2)簡化了物件,使得物件不需要鏈的結構。
  • (3)增加系統的靈活性,通過改變鏈內的成員或者調動他們的次序,允許動態地新增或者刪除處理者
  • (4)增加新的請求處理類很方便。

但是責任鏈模式也存在一些缺點:

  • (1)不能保證請求一定被成功處理
  • (2)系統效能將受到一定影響,並且可能會造成迴圈呼叫。
  • (3)可能不容易觀察執行時的特徵,而且在進行程式碼偵錯時不太方便,有礙於除錯。

        責任鏈模式的UML結構圖如下:

責任鏈模式詳情:Java設計模式之行為型:責任鏈模式

16、行為型-觀察者模式:

        觀察者模式又稱為 釋出-訂閱模式,定義了物件之間一對多依賴關係,當目標物件(被觀察者)的狀態發生改變時,它的所有依賴者(觀察者)都會收到通知。一個觀察目標可以對應多個觀察者,而這些觀察者之間沒有相互聯絡,所以能夠根據需要增加和刪除觀察者,使得系統更易於擴充套件,符合開閉原則;並且觀察者模式讓目標物件和觀察者鬆耦合,雖然彼此不清楚對方的細節,但依然可以互動,目標物件只知道一個具體的觀察者列表,但並不認識任何一個具體的觀察者,它只知道他們都有一個共同的介面。

        但觀察者模式的缺點在於如果存在很多個被觀察者的話,那麼將需要花費一定時間通知所有的觀察者,如果觀察者與被觀察者之間存在迴圈依賴的話,那麼可能導致系統崩潰,並且觀察者模式沒有相應的機制讓觀察者知道被觀察物件是怎麼發生變化的,而僅僅只是知道觀察目標發生了變化。觀察者模式的UML結構圖如下:

 觀察者模式詳情:Java設計模式之行為型:觀察者模式

17、行為型-存取者模式:

        存取者模式就是一種分離物件資料結構與行為 (基於資料結構的操作) 的方法,通過這種分離,達到為一個被存取者動態新增新的操作而無需做其它修改的效果,使得新增作用於這些資料結構的新操作變得簡單,並且不需要改變各資料結構,為不同型別的資料結構提供多種存取操作方式,這樣是存取者模式的設計動機。

        除了使新增存取操作變得更加簡單,也能夠在不修改現有類的層次結構下,定義該類層次結構的操作,並將有關元素物件的存取行為集中到一個存取者物件中,而不是分散搞一個個的元素類中。

       但存取者模式的缺點在於讓增加新的元素類變得困難,每增加一個新的元素類都意味著要在抽象存取者角色中增加一個新的抽象操作,並在每一個具體存取者類中增加相應的具體操作,違背了「開閉原則」的要求;

        所以存取者模式適用於物件結構中很少改變,但經常需要在此物件結構上定義新的操作的系統,使得演演算法操作的增加變得簡單;或者需要對一個物件結構中進行很多不同並且不相關的操作,並且需要避免讓這些操作汙染這些物件,也不希望在增加新操作時修改這些類的場景。

        存取者模式的UML結構圖如下:

        從上面的 UML 結構圖中我們可以看出,存取者模式主要分為兩個層次結構,一個是存取者層次結構,提供了抽象存取者和具體存取者,主要用於宣告一些操作;一個是元素層次結構,提供了抽象元素和具體元素,主要用於宣告 accept 操作;而物件結構 ObjectStructure 作為兩者的橋樑,儲存了不同型別的物件,以便不同的存取者來存取,相同存取者可以以不同的方式存取不同的元素,所以在存取者模式中增加新的存取者無需修改現有程式碼,可延伸行強。

        在存取者模式使用了雙分派技術,所謂雙分派技術就是在選擇方法的時候,不僅僅要根據訊息接收者的執行時區別,還要根據引數的執行時區別。在存取者模式中,使用者端將具體狀態當做引數傳遞給具體存取者,這裡完成第一次分派,然後具體存取者作為引數的「具體狀態」中的方法,同時也將自己this作為引數傳遞進去,這裡就完成了第二次分派。雙分派意味著得到的執行操作決定於請求的種類和接受者的型別。

 存取者模式詳情:Java設計模式之行為型:存取者模式

18、行為型-中介者模式:

         中介者模式通過中介者物件來封裝一系列的物件互動,將物件間複雜的關係網狀結構變成結構簡單的以中介者為核心的星形結構,物件間一對多的關聯轉變為一對一的關聯,簡化物件間的關係,便於理解;各個物件之間的關係被解耦,每個物件不再和它關聯的物件直接發生相互作用,而是通過中介者物件來與關聯的物件進行通訊,使得物件可以相對獨立地使用,提高了物件的可複用和系統的可延伸性。

        在中介者模式中,中介者類處於核心地位,它封裝了系統中所有物件類之間的關係,除了簡化物件間的關係,還可以對物件間的互動進行進一步的控制。中介者模式的UML結構圖如下:

        但是,中介者物件封裝了物件之間的關聯關係,導致中介者物件變得比較龐大複雜,所承擔的責任也比較多,維護起來也比較困難,它需要知道每個物件和他們之間的互動細節,如果它出問題,將會導致整個系統都會出問題。

中介者模式詳情:Java設計模式之行為型:中介者模式

19、行為型-命令模式:

        命令模式的本質是將請求封裝成物件,將發出命令與執行命令的責任分開,命令的傳送者和接收者完全解耦,傳送者只需知道如何傳送命令,不需要關心命令是如何實現的,甚至是否執行成功都不需要理會。命令模式的關鍵在於引入了抽象命令介面,傳送者針對抽象命令介面程式設計,只有實現了抽象命令介面的具體命令才能與接收者相關聯。

        使用命令模式的優勢在於降低了系統的耦合度,而且新命令可以很方便新增到系統中,也容易設計一個組合命令。但缺點在於會導致某些系統有過多的具體命令類,因為針對每一個命令都需要設計一個具體命令類。

        命令模式的UML結構圖如下:

命令模式詳情: Java設計模式之行為型:命令模式

20、行為型-狀態模式:

        狀態模式,就是允許物件在內部狀態發生改變時改變它的行為,物件看起來就好像修改了它的類,也就是說以狀態為原子來改變它的行為,而不是通過行為來改變狀態。

        當物件的行為取決於它的屬性時,我們稱這些屬性為狀態,那該物件就稱為狀態物件。對於狀態物件而言,它的行為依賴於它的狀態,比如要預訂房間,只有當該房間空閒時才能預訂,想入住該房間也只有當你預訂了該房間或者該房間為空閒時。對於這樣的一個物件,當它的外部事件產生互動的時候,其內部狀態就會發生變化,從而使得他的行為也隨之發生變化。

        狀態模式的UML結構圖如下:

 從上面的UML結構圖我們可以看出狀態模式的優點在於:

(1)封裝了轉換規則,允許狀態轉換邏輯與狀態物件合成一體,而不是某一個巨大的條件語句塊

(2)將所有與狀態有關的行為放到一個類中,可以方便地增加新的狀態,只需要改變物件狀態即可改變物件的行為。 

但是狀態模式的缺點在於:

(1)需要在列舉狀態之前需要確定狀態種類

(2)會導致增加系統類和物件的個數。

(3)對 「開閉原則」 的支援並不友好,新增狀態類需要修改那些負責狀態轉換的原始碼,否則無法切換到新增狀態;而且修改某個狀態類的行為也需修改對應類的原始碼。

所以狀態模式適用於:程式碼中包含大量與物件狀態有關的條件語句,以及物件的行為依賴於它的狀態,並且可以根據它的狀態改變而改變它的相關行為。

狀態模式詳情:Java設計模式之行為型:狀態模式

21、行為型-備忘錄模式:

        備忘錄模式提供了一種恢復狀態的機制,在不破壞封裝的前提下,捕獲物件的某個時刻內部狀態,並儲存在該物件之外,保證該物件能夠恢復到某個歷史狀態;備忘錄模式將儲存的細節封裝在備忘錄中,除了建立它的建立者之外其他物件都不能存取它,並且實現了即使要改變儲存的細節也不影響使用者端。但是備忘錄模式都是多狀態和多備份的,會早用較多的記憶體,消耗資源。備忘錄模式的額UML結構圖如下:

         備忘錄模式的核心就是備忘錄 Memento,在備忘錄中儲存的就是原發器 Originator 的部分或者所有的狀態資訊,而這些狀態資訊是不能夠被其他物件所存取的,也就是說我們是不能使用備忘錄之外的物件來儲存這些狀態資訊,如果暴漏了內部狀態資訊就違反了封裝的原則,故備忘錄除了原發器外其他物件都不可以存取。所以為了實現備忘錄模式的封裝,我們需要對備忘錄的存取做些控制:

(1)對原發器:可以存取備忘錄裡的所有資訊。

(2)對負責人 caretaker:不可以存取備忘錄裡面的資料,但是他可以儲存備忘錄並且可以將備忘錄傳遞給其他物件。

(3)其他物件:不可存取也不可以儲存,它只負責接收從負責人那裡傳遞過來的備忘錄同時恢復原發器的狀態。

備忘錄模式詳情:Java設計模式之行為型:備忘錄模式

22、行為型-迭代器模式:

        迭代器模式提供一種存取集合中的各個元素,而不暴露其內部表示的方法。將在元素之間遊走的職責交給迭代器,而不是集合物件,從而簡化集合容器的實現,讓集合容器專注於在它所應該專注的事情上,更加符合單一職責原則,避免在集合容器的抽象介面層中充斥著各種不同的遍歷操作。迭代器模式的UML結構圖如下:

迭代器模式詳情:Java設計模式之行為型:迭代器模式

23、行為型-直譯器模式:

        直譯器模式,就是定義語言的文法,並建立一個直譯器來解釋該語言中的句子,通過構建直譯器,解決某一頻繁發生的特定型別問題範例。

        直譯器模式描述瞭如何構成一個簡單的語言直譯器,主要應用在使用物件導向語言開發的編譯器中,它描述瞭如何為簡單的語言定義一個文法,如何在該語言中表示一個句子,以及如何解釋這些句子。    

        直譯器模式中除了能夠使用文法規則來定義一個語言,還能通過使用抽象語法樹來更加直觀表示、更好地地表示一個語言的構成,每一顆抽象語法樹對應一個語言範例。抽象語法樹描述瞭如何構成一個複雜的句子,通過對抽象語法樹的分析,可以識別出語言中的終結符和非終結符類。 在直譯器模式中由於每一種終結符表示式、非終結符表示式都會有一個具體的範例與之相對應,所以系統的擴充套件性比較好。

        直譯器模式的UML如下:

 直譯器模式詳情:Java設計模式之行為型:直譯器模式


相關推薦閱讀:

Spring常見面試題總結

SpringMVC常見面試題總結

Mybatis常見面試題總結

MySQL常見面試題總結

Redis常見面試題總結

RabbitMQ訊息佇列常見面試題總結

ElasticSearch搜尋引擎常見面試題總結

計算機網路常見面試題總結

作業系統常見面試題總結

Java基礎、集合、多執行緒常見面試題總結

Java虛擬機器器常見面試題總結

Java常見設計模式總結

海量資料處理的方法總結


參考文章:

Java之美[從菜鳥到高手演變]之設計模式

Java之美[從菜鳥到高手演變]之設計模式二

Java之美[從菜鳥到高手演變]之設計模式三

Java之美[從菜鳥到高手演變]之設計模式四