Java中23種設計模式詳細解析

Java開發中23種設計模式詳細介紹

設計模式介紹

設計模式分類

設計模式六大原則

開閉原則(Open Close Principle)

里氏代換原則(Liskov Substitution Principle)

依賴倒轉原則(Dependence Inversion Principle)

介面隔離原則(Interface Segregation Principle)

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

合成複用原則(Composite Reuse Principle)

Java中23種設計模式

建立型模式

工廠方法模式(Factory Method)

普通工廠模式

多個工廠方法模式

靜態工廠方法模式

總結

抽象工廠模式(Abstract Factory)

單例模式(Singleton)

建造者模式(Builder)

原型模式(Prototype)

結構型模式

介面卡模式(Adapter Pattern)

類的介面卡模式

物件的介面卡模式

介面的介面卡模式

裝飾器模式(Decorator)

代理模式(Proxy)

外觀模式(Facade)

橋接模式(Bridge)

組合模式(Composite)

享元模式

行為型模式

策略模式(Strategy)

模板方法模式(Template Method)

觀察者模式(Observer)

迭代子模式(Iterator)

責任鏈模式(Chain of Responsibility)

命令模式(Command)

備忘錄模式(Memento)

狀態模式(State)

訪問者模式(Visitor)

中介者模式(Mediator)

直譯器模式(Interpreter)

設計模式介紹

設計模式(Design Patterns):

一套被

反覆使用,多數人知曉,經過分類編目,程式碼設計

的總結使用設計模式是為了可重用程式碼,讓程式碼更容易理解,保證程式碼可靠性

專案中合理運用設計模式可以完美的解決很多問題,每種模式都有相應的原理與之對應,

每個模式描述了一個在我們周圍不斷重複發生的問題,以及該問題的核心解決方案

設計模式分類

總體來說,設計模式分為三大類:

建立型模式(5種):工廠方法模式抽象工廠模式單例模式建造者模式原型模式

結構型模式(7種):介面卡模式裝飾器模式代理模式外觀模式橋接模式組合模式享元模式

行為型模式(11種):策略模式模板方法模式觀察者模式迭代子模式責任鏈模式命令模式備忘錄模式狀態模式訪問者模式中介者模式直譯器模式

其餘兩類模式:

併發型模式

執行緒池模式

Java中23種設計模式詳細解析

設計模式六大原則

單一職責原則(Single Responsibility Principle) - 這裡的設計模式原則,主要討論的是Java面向物件程式設計設計中設計原則,單一職責原則由於其適用的普遍性,個人認為不放在六大原則之中

單一職責原則

:一個類只負責一項職責

不能存在多於一個導致類變更的原因單一職責原則符合“高內聚,低耦合”的思想單一職責原則不只是面向物件程式設計思想所特有的,只要是模組化的程式設計,都適用單一職責原則

開閉原則(Open Close Principle)

開閉原則

:對擴充套件開放,對修改關閉

程式進行擴充套件的時候,不能修改原有的程式碼,

實現一個熱插拔的效果

為了使程式擴充套件性好,易於維護和升級:需要

使用介面和抽象類

里氏代換原則(Liskov Substitution Principle)

里氏代換原則

:任何基類可以出現的地方,子類一定可以出現

LSP是

繼承複用

的基石,只有

當衍生類可以替換掉基類,軟體單位的功能不受影響時,

基類才能真正被複用,衍生類也能夠在基類的基礎上增加新的行為

里氏代換原則是對實現抽象化的具體步驟的規範:

里氏代換原則是對開閉原則的補充實現開閉原則的關鍵步驟就是抽象化基類與子類的繼承關係就是抽象化的具體實現

依賴倒轉原則(Dependence Inversion Principle)

依賴倒轉原則

:針對介面程式設計,依賴於抽象而不依賴於具體

依賴倒轉原則是開閉原則的基礎

介面隔離原則(Interface Segregation Principle)

介面隔離原則

:使用多個隔離的介面,比使用單個介面要好,降低類之間的耦合度

從介面隔離原則可以看出:設計模式就是一個軟體的設計思想

從大型軟體架構出發,為了升級和維護方便

:降低依賴,降低耦合

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

迪米特法則:最少知道原則

,一個實體應當儘量少的與其它實體發生相互作用,使得功能模組相互獨立

合成複用原則(Composite Reuse Principle)

合成複用原則

:儘量使用合成或者聚合的方式,而不是使用繼承

Java中23種設計模式詳細解析

Java中23種設計模式

建立型模式

工廠方法模式(Factory Method)

工廠方法模式分為三種

:普通工廠模式,多個工廠方法模式,靜態工廠方法模式

普通工廠模式

建立一個工廠類,對實現了同一介面的一些類進行例項的建立

多個工廠方法模式

多個工廠方法模式是對普通工廠方法模式的改進

普通工廠方法模式中,如果傳遞的字串出錯,則不能建立物件

多個工廠方法模式提供多個工廠方法,分別建立物件

靜態工廠方法模式

多個工廠方法模式

中的方法設定為

靜態方法,

不需要建立例項

,直接呼叫即可

總結

工廠模式適合出現大量的產品需要建立,並且具有共同的介面,可以透過工廠方法模式建立:普通工廠模式:

如果傳入字串有誤,就不能建立物件

靜態工廠方法模式

相對於多個工廠方法模式

,不需要例項化工廠類大多數情況下,採用靜態工廠方法模式

抽象工廠模式(Abstract Factory)

工廠方法模式問題:

類的建立依賴工廠類。如果想要擴充套件程式,必須對工廠類進行修改,這違背了閉包原則

抽象工廠模式:

建立多個工廠類,一旦需要增加新的功能,直接增加工廠類就可以,不需要修改之前的工廠類

抽象工廠模式的優點就是拓展性強:

如果需要增加一個功能,例如:發及時資訊只需做一個

實現類,

實現Sender介面做一個

工廠類,

實現Provider介面

單例模式(Singleton)

單例模式

:保證在一個JVM中,一個單例物件只有一個例項存在

單例模式的優點:

某些類建立比較繁瑣,對於一些大型物件,可以減少很大的系統開銷省去了new運算子,降低了系統記憶體的使用頻率,減輕GC(Garbage Collection-垃圾回收)壓力有些類比如交易所的核心交易引擎,控制著交易流程,如果該類可以建立多個,系統會完全混亂,所有隻有使用單例模式,才能保證核心交易伺服器獨立控制整個流程

考慮到多執行緒安全,首先會想到對

getInstance

方法加

synchronized

關鍵字

由於

synchronized

鎖住的是這個物件,這樣的用法,每次呼叫getInstance(),都要對物件上鎖,在效能上會有所下降。

只有在第一次建立物件的時候需要加鎖,之後就不需要了

這樣似乎解決了問題,將

synchronized

關鍵字加入內部,這樣在呼叫的時候是不需要加鎖的,只有在instance為null,並建立物件的時候才需要的加鎖,效能得到了提升,但是這樣的情況還是有問題的

存在這樣的情況:

在Java中建立物件和賦值操作是分開進行的即instance=new Singleton()是分兩步執行的JVM並不保證這兩個操作的先後順序:有可能JVM會為新的Singleton例項分配空空間,然後直接賦值給instance成員然後再去初始化這個Singleton例項這樣就可能會出錯

示例:

A,B兩個執行緒A,B執行緒同時進入第一個if判斷A首先進入

synchronized

塊,由於

instance

null,

執行

instance=new Singleton()

由於JVM內部的最佳化機制

,JVM

先劃出一些分配給

Singleton

的空白記憶體,並賦值給

instance

成員,此時還沒有開始初始化這個例項,然後A離開了

synchronized

塊B進入

synchronized,

由於i

nstance

此時不是

null,

因此它馬上離開了

synchronized

塊並將結果返回給呼叫該方法的程式此時B執行緒打算使用

Singleton

例項,發現它還沒有初始化,於是產生錯誤

程式碼需要進一步最佳化

實際情況是:

單例模式使用內部類來維護單例的實現

JVM內部的機制能夠保證當一個類被載入的時候,這個類的載入過程是互斥的

當第一次呼叫getInstance時,JVM能夠保證instance只被建立一次,並且會保證把賦值給instance的記憶體初始化完畢

該方法也只會在第一次呼叫的時候採用互斥機制,可以完美解決低效能的問題

這種方法,如果在建構函式中丟擲異常,例項將永遠不會建立,也會出錯。

只能根據實際場景,選擇最適合應用場景的實現方法

因為只需要在建立類的時候進行同步,所以只要將建立和getInstance()分開,單獨為建立加synchronized關鍵字,也是可以的

採用

“影子例項”

的方法為單例物件的屬性

同步更新

單例模式的特點:

單例模式理解起來簡單,具體實現起來是有一定難度的\同步非同步synchronized關鍵字鎖定的是物件,使用的時候要在恰當的地方使用:注意需要使用鎖的物件和過程,有時候不是整個物件及整個過程都需要鎖

採用類的靜態方法,可以實現單例模式的效果類的靜態方法和單例模式的區別:靜態類不能實現介面:

從類的角度說,是可以的,但是這樣就會破壞靜態了介面中不允許有static修飾的方法,即使實現了也是非靜態的

單例可以被延遲啟動:

靜態類在第一次載入時初始化單例延遲載入,是因為有些類比較龐大,延遲載入有助於提升效能

單例可以被繼承:

單例中的方法可以被重寫靜態類內部方法都是static,無法重寫

單例比較靈活:

從實現上講,單例只是一個普通的Java類,只要滿足單例的基本要求,可以隨心所欲地實現其它功能靜態類不行

單例模式內部可以就是用一個靜態類實現

Java中23種設計模式詳細解析

建造者模式(Builder)

工廠模式提供的是建立單個類的模式

建造者模式:

將各種產品集中起來進行管理,用來建立複合物件複合物件:

指某個類具有不同的屬性

建造者模式就是抽象工廠類模式和Test類結合起來得到的

程式碼實現:

一個

Sender

介面,兩個實現類

MailSender

SmsSender

建造者模式將很多功能整合到一個類裡,這個類就可以創造出比較複雜的模組

建造者模式和工廠模式的區別:

工廠模式關注的是建立單個產品建造者模式關注的是建立符合物件,多個部分

原型模式(Prototype)

原型模式:

將一個物件作為原型,進行復制,克隆,產生一個和原物件類似的新物件

原型模式雖然是建立型模式,但是與工廠模式沒有關係

在Java中,複製物件是透過

clone()

實現的

一個原型類,只需要實現

Cloneable

介面,重寫

clone()

方法

clone

方法可以改寫為任何名稱,因為

Cloneable

介面是個空介面,可以任意定義實現類的方法名

重點是

super.clone():super.clone()

方法呼叫的是

Object

的**clone() ** 方法在Object類中

,clone()

方法時

native

物件的深複製和淺複製:深複製:

將一個物件複製後,不論是基本型別還是引用型別,都是重新建立的深複製會進行完全徹底的複製

淺複製:

將一個物件複製後,基本資料型別的變數都會重新建立,而引用型別指向的還是原物件的引用

要實現深複製:

要採用

流的形式

讀入當前物件的二進位制輸入

再寫出二進位制資料對應的物件

結構型模式

介面卡模式(Adapter Pattern)

物件的介面卡模式

是各種結構型模式的起源

介面卡模式:

將某個類的介面轉換成客戶端期望的另一個介面表示

目的:

消除由於介面不匹配所造成的類的相容性問題

介面卡模式主要分為三類:

類的介面卡模式物件的介面卡模式介面的介面卡模式

類的介面卡模式

Java中23種設計模式詳細解析

核心思想:

有一個

Source

類,擁有一個方法待適配,目標介面是

Targetable,

透過

Adapter

類,將

Source

的功能擴充套件到

Targetable

Adapter類繼承Source類,實現Targetable介面

這樣Targetable介面的實現類就具有Source類的功能

物件的介面卡模式

基本思路和類的介面卡相同,只是將Adapter類作修改

,不繼承Source類,而是持有Source類的例項,以達到解決相容性問題

介面的介面卡模式

一個介面中有多個抽象方法,當寫該介面的實現類時,必須實現該介面的所有方法,這樣明顯比較浪費,因為並不是所有的方法都是需要用到的,有時只要引入一些即可。為了解決這樣的問題,引入了介面介面卡模式

介面介面卡模式:

藉助於一個抽象類,該抽象類實現了該介面以及所有的方法,只需要和該抽象類進行聯絡即可.

只需要寫一個類,繼承該抽象類,重寫需要用到的方法

三種介面卡模式的應用場景:類的介面卡模式:

當希望一個類轉換成滿足另一個新介面的類時,可以使用類的介面卡模式建立一個新類,繼承原有的類,實現新的介面即可

物件的介面卡模式:

當希望一個物件轉換成滿足另一個新介面的物件時,可以使用物件的介面卡模式建立一個Wrapper類,持有原類的一個例項,在Wrapper類的方法中,呼叫例項的方法即可

介面的介面卡模式:

當不希望實現一個介面中所有的方法時,可以使用介面的介面卡模式建立一個抽象類Wrapper,實現所有方法,寫其它類時,只要繼承抽象類即可

裝飾器模式(Decorator)

裝飾器模式:

給一個物件動態地增加一些新的功能

裝飾器模式要求裝飾物件和被裝飾物件

實現同一個介面,

裝飾物件持有

被裝飾物件的例項

Source

類時被裝飾類

,Decorator

類是裝飾類,可以為

Source

類動態地增加一些功能

裝飾器模式應用場景:

需要擴充套件一個類的功能動態地為一個物件增加功能,而且還能動態地撤銷(繼承的功能是靜態的,不能動態增刪)

裝飾器模式的缺點:

產生過多類似的物件,不易排錯

代理模式(Proxy)

代理模式:

建立一個代理類,替原物件進行一些操作

代理模式的應用場景:

已有的方法在使用的時候需要對原有的方法進行改進,有兩種方法:

修改原有的方法來適應:

這樣違反了“對擴充套件開放,對修改關閉”的原則

.不推薦使用採用一個代理類呼叫原有的方法,且對產生的結果進行控制.

即代理模式

使用代理模式,可以將功能劃分的更加清晰,有助於後期維護

外觀模式(Facade)

在Spring中,可以將類與類之間的關係配置到配置檔案中

外觀模式:

為了解決類與類之間的依賴關係,將類魚雷之間的關係放到一個Facade類中,降低類與類之間的耦合度,該模式中沒有涉及到介面

如果沒有

Computer

,CPU,Memory,Disk

之間會互相持有例項,產生關係,這樣會造成嚴重依賴

修改一個類,可能會帶來其它類的修改

有了

Computer

類,各個類之間的關係就放在類Computer類裡,這樣就起到

解耦

的作用

橋接模式(Bridge)

橋接模式:

將事物和具體實現分開,二者可以各自獨立的變化

將抽象化與實現化解耦,使得二者可以獨立變化:

JDBC橋

DriverManager:

JDBC連線資料庫的時候,在各個資料庫之間進行切換,基本不需要改動太多的程式碼,甚至一點不用改動原因在於JDBC提供統一介面,每個資料庫提供各自實現,用一個叫作資料庫驅動的程式來橋接即可

透過對Bridge類的呼叫,實現了對介面Sourceable的實現類SourceSub1和SourceSub2的呼叫

示例:

JDBC連線原理

組合模式(Composite)

組合模式:

部分-整體模式,在處理類似樹形結構的問題時比較方便

組合模式使用場景:

將多個物件組合在一起進行操作

常用於表示樹形結構中:二叉樹

享元模式

享元模式:

主要目的是實現物件共享,即共享池

當系統中物件多的時候可以減少記憶體的開銷,通常與工廠模式一起使用

FlyWeightFactory:

負責建立和管理享元單元當一個客戶端請求時,工廠需要檢查當前物件池中是否有符合條件的物件如果有,就返回已經存在的物件如果沒有,就建立一個新物件

FlyWeight:

超類

共享的物件的特點:

共享物件有一些共同的屬性這些屬性對於每個連線來說都是一樣的

基於共享物件的特點,可以用享元模式處理共享物件:

將類似屬性作為內部資料

其它的屬性作為外部資料

在方法呼叫時,當作引數傳進來

這樣可以節省記憶體空間,減少例項的數量

示例:

資料庫連線池

透過連線池的連線,實現資料庫連線的共享:

不需要每一次重新建立連線,節省資料庫重新建立的開銷,提升了系統系能

行為型模式

11種行為模式的關係:

第一類: 透過父類與子類的關係進行實現

第二類: 透過兩個類之間的關係進行實現

第三類: 透過類的狀態進行實現

第四類: 透過中間類進行實現

策略模式(Strategy)

策略模式:

定義了一系列演算法,並將每個演算法封裝起來,可以相互替換,演算法的變化不會影響到使用演算法的使用者

設計一個介面,為一系列實現類提供統一的方法,多個實現類實現該介面

設計一個抽象類(選用,作為輔助類),提供輔助函式

ICalculator

介面提供統一的方法

AbstractCalculator

是抽象輔助類,提供輔助方法

策略模式的決定權在於使用者:

系統本身提供不同演算法的實現新增或刪除演算法對各種演算法做封裝

策略模式多用在演算法決策系統中,外部使用者只需要決定使用哪一個演算法即可

模板方法模式(Template Method)

模板方法模式:

一個抽象類中,定義一個主方法

再定義無數個方法,可以是抽象的,也可以是實際的方法

定義一個子類,繼承抽象類,重寫抽象類中的方法

透過呼叫抽象類,實現對子類的呼叫

AbstractCalculator

類中定義一個主方法

calculate()

calculate()

呼叫

split()

Plus

Minus

分別繼承

AbstractCalculator

透過對

AbstractCalculator

的呼叫實現對子類的呼叫

Test的執行過程:

首先將

exp

和“\ \ +”做引數,呼叫

AbstractCalculator

類裡的**calculate(String,String)**方法

calculate(String,String)

裡呼叫同類的

split()

然後再呼叫

calculate(int,int)

方法

從這個方法進入到子類中

執行完

return num1+num2

之後,將值返回到

AbstractCalculator

類,賦值給

result,

打印出來

觀察者模式(Observer)

觀察者模式是類與類之間的關係,不涉及繼承

觀察者模式類似郵件訂閱和RSS訂閱:

當你訂閱了該內容,如果後續有更新,會及時接收到通知

觀察者模式:

當一個物件變化時,依賴該物件的物件都會接收到通知,並且隨著變化.物件之間是一對多的關係

MySubject

類是主物件

Observer1

Observer2

是依賴於

MySubject

的物件

MySubject

變化時

,Observer1

Observer2

必然變化

AbstractSubject

類中定義者需要監控的物件列表,可以對這些物件進行修改:增加或者刪除被監控物件

MySubject

變化時

,AbstractSubject

類負責通知在列表記憶體在的物件

觀察者模式根據關係圖,新建專案,使用程式碼按照總體思路走一遍,這樣容易理解觀察者模式的思想

迭代子模式(Iterator)

迭代子模式是類與類之間的關係,不涉及繼承

迭代子模式:

順序訪問聚集中的物件.

包含兩層意思:

聚集物件:

需要遍歷的物件

迭代器物件:

用於對聚集物件進行遍歷訪問

MyCollection

中定義了集合的一些操作

MyIterator

中定義了一系列迭代操作,並且持有

Collection

例項

JDK中各個類都是這些基本的集合,加上一些設計模式,再加一些最佳化組合到一起的,只要掌握這些,可以寫出自定義的集合類,甚至框架

Java中23種設計模式詳細解析

責任鏈模式(Chain of Responsibility)

類與類之間的關係,不涉及繼承

責任鏈模式:

有多個物件,每個物件持有對下一個物件的引用,這樣形成一條鏈,直到某個物件決定處理該請求

請求發出者並不清楚到底最終哪個物件會處理該請求

責任鏈模式可以實現

:在隱瞞客戶端的情況下,對系統進行動態調整

Abstracthandler

類提供了

get

set

方法,方便

MyHandler

類設定和修改引用物件

MyHandler

類是核心,例項化後生成一系列相互持有的物件,構成一條鏈

連結上的請求可以是一條鏈,可以是一個樹,還可以是一個環

模式本身不受這個約束,需要自定義實現

在同一個時刻,命令只允許由一個物件傳給另一個物件,不允許傳給多個物件

命令模式(Command)

類與類之間的關係,不涉及繼承

命令模式理解示例:

司令員的作用是: 發出口令口令經過傳遞,傳到士兵耳中,士兵去執行這個過程好在:司令,口令,士兵三者相互解藕任何一方都不用去依賴其它方,只需要做好自身的事即可司令員要的是結果,不會去關注士兵到底怎麼實現的

Invoker

是呼叫者(司令員)

Receiver

是被呼叫者(士兵)

MyCommand

是命令,實現了

Command

介面,持有接收物件

命令模式的目的:

達到命令的發出者和執行者之間的解耦,實現請求和執行分開

Struts其實就是一種將請求和呈現分離的技術,使用了命令模式的設計思想

備忘錄模式(Memento)

備忘錄模式

:主要目的是儲存一個物件的某個狀態,以便在適當的時候恢復物件

備忘錄模式理解:

假設有原始類A,A中有各種屬性,A可以決定需要備份的屬性備忘錄類B用來儲存A的一些內部狀態類C用來儲存備忘錄,並且只能儲存,不能進行修改等操作

Original

類是原始類,裡面有需要儲存的屬性

value

及建立一個備忘錄類,用來儲存

value

Memento

類是備忘錄類

Storage

類是儲存備忘錄的類,持有

Memento

類的例項

新建原始類時

,value

被初始化為

egg,

後經過修改,將

value

值修改為

bulk,

最後進行恢復狀態,結果成功恢復

狀態模式(State)

狀態模式

:當物件的狀態改變時,同時改變物件的行為

狀態模式理解示例:

QQ有幾種不同的狀態:線上,隱身,忙碌等每個狀態對應不同的操作,而且好友也能看到相關的狀態

狀態模式包括兩層含義:

可以透過改變狀態來獲得不同的行為

物件狀態的變化可以被發現

State

類是個狀態類

Context

類可以實現切換

狀態模式的應用場景十分廣泛:在做網站的時候,希望根據物件的屬性,區別一些功能等,比如說許可權控制等等

訪問者模式(Visitor)

訪問者模式將資料結構和作用於結構上的操作解耦,使得操作集合可以相對自由地進行演化

訪問者模式適用於資料結構相對穩定,演算法容易變化的系統:

訪問者模式使得演算法操作增加變得更加容易

若系統資料結構物件易於變化,經常有新的物件增加進來,則不適合使用訪問者模式

訪問者模式的特點:優點:

增加操作容易增加操作意味著增加新的訪問者訪問者模式將有關行為集中到一個訪問者物件中, 這些行為的改變不影響系統資料結構

缺點:

增加新的資料結構很困難

訪問者模式

:是一種分離物件資料結構和行為的方法,透過這種分離,可以達到為一個被訪問者動態新增新的操作而無需做任何修改的效果

一個

Visitor

類,存放要訪問的物件

Subject

類中有

accept

方法,接收將要訪問的物件

,getSubject()

獲取將要被訪問的屬性

訪客模式適用場景:

如果要為一個現有的類增加新功能:新功能是否會與現有功能出現相容性問題以後會不會還有新功能需要新增如果類不允許修改程式碼怎麼處理這些問題最好的解決辦法就是訪客模式

訪問者模式適用於資料結構相對穩定的系統,將資料結構和演算法解耦

中介者模式(Mediator)

中介者模式是用來降低類與類之間的耦合的:

類與類之間有依賴關係的話,不利於功能的拓展和維護因為只要修改一個物件,其它關聯的物件都要進行修改

中介者模式:

只需要關心Mediator類的關係,具體類與類之間的關係及排程交給Mediator,

與Spring容器的作用類似

User

類統一介面

User1

User2

分別是不同的物件:二者之間有關聯如果不採用中介者模式。則需要二者相互持有引用,這樣二者的耦合度很高為了解耦,引入了

Mediator

類,提供統一介面

MyMediator

為實現類:持有

User1

User2

的例項,用來實現對

User1

User2

的控制這樣

User1

User2

兩個物件就可以相互獨立,只需保持與

Mediator

之間的關係就可以

Mediator

類用來維護

直譯器模式(Interpreter)

直譯器模式一般主要應用在OOP開發中的編譯器開發中,適用面比較窄

Context

類是一個上下文環境類

Plus

Minus

分別是計算的實現

直譯器模式是來用作各種各樣的直譯器