引言
對於設計模式,應該明白不同的設計用來解決什么場景問題,對於常用的設計模式能夠靈活運用。
設計模式分類
模式分類有助於更快地學習模式,並且對發現新的模式也有指導作用。
根據兩條原則進行分類。
第一是目的准則,即模式是用來完成什么工作的。模式依據其目的分為創建型、結構型、行為型三種。
創建型模式與對象的創建有關;結構型模式處理類或對象的組合;行為型模式對類或對象怎樣交互和怎樣分配職責進行描述。
第二條是范圍准則,指定模式主要是用於類還是用於對象。
類模式處理類和子類之間的關系,這些關系通過繼承建立,是靜態的,在編譯時刻便確定下來了。
對象模式處理對象之間的關系,這些關系在運行時刻是可以變化的,更具動態性。
從某種意義上來說,幾乎所有模式都是用繼承機制,所以“類模式”只指那些集中於處理類間關系的模式,而大部分模式都屬於對象模式的范疇。
| 類模式 |
對象模式 |
|
| 創建型 |
將對象的部分創建工作延遲到子類 |
將對象部分的創建工作延遲到另一個對象中 |
| 結構型 |
使用繼承機制來組合類 |
描述了對象的組裝方式 |
| 行為型 |
使用繼承描述算法和控制流 |
描述一組對象怎樣協作完成單個對象所無法完成的任務 |
還有其他組織模式的方式。有些模式經常會被綁在一起使用,有些模式是可替代的,有些模式盡管使用意圖不同但產生的設計結果是很相似的。
還有一種方式是根據模式的“相關模式”部分所描述的他們怎樣互相引用來組織設計模式。
顯然存在着許多組織設計模式的方法。從多角度思考模式有助於對他們的功能、差異和應用場合的更深入理解。

創建型
- 單例模式
- 工廠方法模式
- 抽象工廠模式
- 建造者模式
- 原型模式
結構型
- 適配器模式
- 裝飾器模式
- 代理模式
- 外觀模式
- 橋接模式
- 組合模式
- 享元模式
行為型
- 策略模式
- 模板方法模式
- 觀察者模式
- 迭代子模式
- 責任鏈模式
- 命令模式
- 備忘錄模式
- 狀態模式
- 訪問者模式
- 中介者模式
- 解釋器模式
下面重點介紹幾種常用的設計模式。
常用設計模式
下面結合設計模式的實際應用,來介紹常用的設計模式,如下圖所示。在面試時遇到類似問題,記得要將設計模式與實際業務場景進行結合,來體現對設計模式的理解和應用能力。
單例模式
首先是單例模式,這個模式在實際業務中會經常用到,也是設計模式中的主要考察點。這里介紹線程安全的單例模式實現方式。
單例模式常見的實現方式有三種。
-
第一種是靜態初始化方式,也叫作餓漢方式。實現的思路就是在類初始化時完成單例實例的創建,因此不會產生並發問題,在這種方式下不管是否會使用到這個單例,都會創建這個單例。
-
第二種實現方式是雙重檢查,也叫作懶漢方式,只有在真正用到這個單例實例的時候才會去創建,如果沒有使用就不會創建。這個方式必然會面對多個線程同時使用實例時的並發問題。為了解決並發訪問問題,通過 synchronized 或者 lock 進行雙重檢查,保證只有一個線程能夠創建實例。這里要注意內存可見性引起的並發問題,必須使用 volatile 關鍵字修飾單例變量。
-
第三種是單例注冊表方式,Spring 中 Bean 的單例模式就是通過單例注冊表方式實現的。
第二種雙重檢查, 代碼示例 SingletonDoubleCheckedLocking.java
package com.xgcd.singletonPattern; /** * 雙重鎖/雙檢鎖/雙重校驗鎖 * <p> * 也是懶加載的方式 * 線程安全 * 這種方式采用雙鎖機制,安全且在多線程情況下能保持高性能。 * getInstance() 的性能對應用程序很關鍵。 */ public class SingletonDoubleCheckedLocking { // 必須使用 volatile 關鍵字修飾單例變量 // 作用: 1.保證可見性。使用 volatile 定義的變量,將會保證對所有線程的可見性。 // 2.禁止指令重排序優化。 // 由於 volatile 禁止對象創建時指令之間重排序,所以其他線程不會訪問到一個未初始化的對象,從而保證安全性。 // 注意,volatile禁止指令重排序在 JDK 5 之后才被修復 private static volatile SingletonDoubleCheckedLocking instance; private SingletonDoubleCheckedLocking() { } public static SingletonDoubleCheckedLocking getInstance() { // 多線程程序中,不用讓每個線程每次都加鎖,而只是在實例未被創建的時候再加鎖處理.同時也能保證多線程的安全.這種做法被稱為 double-checked locking(雙重鎖定) if (instance == null) { synchronized (SingletonDoubleCheckedLocking.class) { // 對於instance情況,直接返回instance // 當instance為null並且同時有兩個線程調用getInstance方法時,他們將都可以通過第一重instance==null的判斷 // 然后由於lock機制,這兩個線程則只有一個進入,另一個在外排隊等候,必須要其中的一個進入並出來后,另一個才能進入 // 而此時如果沒有了第二重的instance==null的判斷,則第一個線程創建了實例,而第二個線程還是可以繼續再創建新的實例,這就沒有達到單例的目的 // 所以采用兩次instance==null的判斷 if (instance == null) { instance = new SingletonDoubleCheckedLocking(); } } } return instance; } }
volatile關鍵字: 為什么雙重檢查鎖模式需要 volatile ?
工廠模式
工廠模式是創建不同類型實例時常用的方式,例如 Spring 中的各種 Bean 是由不同 Bean 工廠類進行創建的。
代理模式
代理模式,主要用在不適合或者不能直接引用另一個對象的場景,可以通過代理模式對被代理對象的訪問行為進行控制。Java 的代理模式分為靜態代理和動態代理。靜態代理指在編譯時就已經創建好了代理類,例如在源代碼中編寫的類;動態代理指在 JVM 運行過程中動態創建的代理類,使用動態代理的方法有 JDK 動態代理、CGLIB、Javassist 等。面試時遇到這個問題可以舉個動態代理的例子,比如在 Motan RPC 中,是使用 JDK 的動態代理,通過反射把遠程請求進行封裝,使服務看上去就像在使用本地的方法。
責任鏈模式
Chain of Responsibility 使多個對象都有機會處理請求,從而避免請求的發送者和接受者之間的耦合關系. 將這個對象連成一條鏈, 並沿着這條鏈傳遞該請求, 直到有一個對象處理它為止.
責任鏈模式有點像工廠的流水線,鏈上每一個節點完成對對象的某一種處理,例如 Netty 框架在處理消息時使用的 Pipeline 就是一種責任鏈模式。
適配器模式
適配器模式,類似於我們常見的轉接頭,把兩種不匹配的對象來進行適配,也可以起到對兩個不同的對象進行解藕的作用。例如我們常用的日志處理框架 SLF4J,如果我們使用了 SLF4J 就可以跟 Log4j 或者 Logback 等具體的日志實現框架進行解藕。通過不同適配器將 SLF4J 與 Log4j 等實現框架進行適配,完成日志功能的使用。
觀察者模式
觀察者模式也被稱作發布訂閱模式,適用於一個對象的某個行為需要觸發一系列事件的場景,例如 gRPC 中的 Stream 流式請求的處理就是通過觀察者模式實現的。
構造者模式
構造者模式,適用於一個對象有很多復雜的屬性,需要根據不同情況創建不同的具體對象,例如創建一個 PB 對象時使用的 builder 方式。
