biancheng-Java設計模式:23種設計模式全面解析(超級詳細)
http://c.biancheng.net/design_pattern/
軟體設計模式的基本要素
軟體設計模式使人們可以更加簡單方便地複用成功的設計和體系結構,它通常包含以下幾個基本要素:模式名稱、別名、動機、問題、解決方案、效果、結構、模式角色、合作關係、實現方法、適用性、已知應用、例程、模式擴充套件和相關模式等,其中最關鍵的元素包括以下 4 個主要部分。
1. 模式名稱
每一個模式都有自己的名字,通常用一兩個詞來描述,可以根據模式的問題、特點、解決方案、功能和效果來命名。模式名稱(PatternName)有助於我們理解和記憶該模式,也方便我們來討論自己的設計。
2. 問題
問題(Problem)描述了該模式的應用環境,即何時使用該模式。它解釋了設計問題和問題存在的前因後果,以及必須滿足的一系列先決條件。
3. 解決方案
模式問題的解決方案(Solution)包括設計的組成成分、它們之間的相互關係及各自的職責和協作方式。因為模式就像一個模板,可應用於多種不同場合,所以解決方案並不描述一個特定而具體的設計或實現,而是提供設計問題的抽象描述和怎樣用一個具有一般意義的元素組合(類或物件的 組合)來解決這個問題。
4. 效果
描述了模式的應用效果以及使用該模式應該權衡的問題,即模式的優缺點。主要是對時間和空間的衡量,以及該模式對系統的靈活性、擴充性、可移植性的影響,也考慮其實現問題。顯式地列出這些效果(Consequence)對理解和評價這些模式有很大的幫助。
GoF 的 23 種設計模式的分類和功能
1. 根據目的來分
根據模式是用來完成什麼工作來劃分,這種方式可分為建立型模式、結構型模式和行為型模式 3 種。
- 建立型模式:用於描述“怎樣建立物件”,它的主要特點是“將物件的建立與使用分離”。GoF 中提供了單例、原型、工廠方法、抽象工廠、建造者等 5 種建立型模式。
- 結構型模式:用於描述如何將類或物件按某種佈局組成更大的結構,GoF 中提供了代理、介面卡、橋接、裝飾、外觀、享元、組合等 7 種結構型模式。
- 行為型模式:用於描述類或物件之間怎樣相互協作共同完成單個物件都無法單獨完成的任務,以及怎樣分配職責。GoF 中提供了模板方法、策略、命令、職責鏈、狀態、觀察者、中介者、迭代器、訪問者、備忘錄、直譯器等 11 種行為型模式。
2. 根據作用範圍來分
根據模式是主要用於類上還是主要用於物件上來分,這種方式可分為類模式和物件模式兩種。
- 類模式:用於處理類與子類之間的關係,這些關係通過繼承來建立,是靜態的,在編譯時刻便確定下來了。GoF中的工廠方法、(類)介面卡、模板方法、直譯器屬於該模式。
- 物件模式:用於處理物件之間的關係,這些關係可以通過組合或聚合來實現,在執行時刻是可以變化的,更具動態性。GoF 中除了以上 4 種,其他的都是物件模式。
表 1 介紹了這 23 種設計模式的分類。
範圍\目的 | 建立型模式 | 結構型模式 | 行為型模式 |
---|---|---|---|
類模式 | 工廠方法 | (類)介面卡 | 模板方法、直譯器 |
物件模式 | 單例 原型 抽象工廠 建造者 |
代理 (物件)介面卡 橋接 裝飾 外觀 享元 組合 |
策略 命令 職責鏈 狀態 觀察者 中介者 迭代器 訪問者 備忘錄 |
3. GoF的23種設計模式的功能
前面說明了 GoF 的 23 種設計模式的分類,現在對各個模式的功能進行介紹。
- 單例(Singleton)模式:某個類只能生成一個例項,該類提供了一個全域性訪問點供外部獲取該例項,其拓展是有限多例模式。
- 原型(Prototype)模式:將一個物件作為原型,通過對其進行復制而克隆出多個和原型類似的新例項。
- 工廠方法(Factory Method)模式:定義一個用於建立產品的介面,由子類決定生產什麼產品。
- 抽象工廠(AbstractFactory)模式:提供一個建立產品族的介面,其每個子類可以生產一系列相關的產品。
- 建造者(Builder)模式:將一個複雜物件分解成多個相對簡單的部分,然後根據不同需要分別建立它們,最後構建成該複雜物件。
- 代理(Proxy)模式:為某物件提供一種代理以控制對該物件的訪問。即客戶端通過代理間接地訪問該物件,從而限制、增強或修改該物件的一些特性。
- 介面卡(Adapter)模式:將一個類的介面轉換成客戶希望的另外一個介面,使得原本由於介面不相容而不能一起工作的那些類能一起工作。
- 橋接(Bridge)模式:將抽象與實現分離,使它們可以獨立變化。它是用組合關係代替繼承關係來實現,從而降低了抽象和實現這兩個可變維度的耦合度。
- 裝飾(Decorator)模式:動態的給物件增加一些職責,即增加其額外的功能。
- 外觀(Facade)模式:為多個複雜的子系統提供一個一致的介面,使這些子系統更加容易被訪問。
- 享元(Flyweight)模式:運用共享技術來有效地支援大量細粒度物件的複用。
- 組合(Composite)模式:將物件組合成樹狀層次結構,使使用者對單個物件和組合物件具有一致的訪問性。
- 模板方法(TemplateMethod)模式:定義一個操作中的演算法骨架,而將演算法的一些步驟延遲到子類中,使得子類可以不改變該演算法結構的情況下重定義該演算法的某些特定步驟。
- 策略(Strategy)模式:定義了一系列演算法,並將每個演算法封裝起來,使它們可以相互替換,且演算法的改變不會影響使用演算法的客戶。
- 命令(Command)模式:將一個請求封裝為一個物件,使發出請求的責任和執行請求的責任分割開。
- 職責鏈(Chain of Responsibility)模式:把請求從鏈中的一個物件傳到下一個物件,直到請求被響應為止。通過這種方式去除物件之間的耦合。
- 狀態(State)模式:允許一個物件在其內部狀態發生改變時改變其行為能力。
- 觀察者(Observer)模式:多個物件間存在一對多關係,當一個物件發生改變時,把這種改變通知給其他多個物件,從而影響其他物件的行為。
- 中介者(Mediator)模式:定義一箇中介物件來簡化原有物件之間的互動關係,降低系統中物件間的耦合度,使原有物件之間不必相互瞭解。
- 迭代器(Iterator)模式:提供一種方法來順序訪問聚合物件中的一系列資料,而不暴露聚合物件的內部表示。
- 訪問者(Visitor)模式:在不改變集合元素的前提下,為一個集合中的每個元素提供多種訪問方式,即每個元素有多個訪問者物件訪問。
- 備忘錄(Memento)模式:在不破壞封裝性的前提下,獲取並儲存一個物件的內部狀態,以便以後恢復它。
- 直譯器(Interpreter)模式:提供如何定義語言的文法,以及對語言句子的解釋方法,即直譯器。
UML統一建模語言是什麼?
基本構件
UML 建模的核心是模型,模型是現實的簡化、真實系統的抽象。UML 提供了系統的設計藍圖。當給軟體系統建模時,需要採用通用的符號語言,這種描述模型所使用的語言被稱為建模語言。在 UML 中,所有的描述由事物、關係和圖這些構件組成。下圖完整地描述了所有構件的關係。
事物
事物是抽象化的最終結果,分為結構事物、行為事物、分組事物和註釋事物。
1. 結構事物
結構事物是模型中的靜態部分,用以呈現概念或實體的表現元素,如下表所示。
事物 | 解釋 | 圖例 |
---|---|---|
類(Class) | 具有相同屬性、方法、關係和語義的物件集合 | |
介面(Interface) | 指一個類或構件的一個服務的操作集合,它僅僅定義了一組操作的規範,並沒有給出這組操作的具體實現 | |
用例(User Case) | 指對一組動作序列的描述,系統執行這些動作將產生一個對特定的參與者(Actor)有價值且可觀察的結果 | |
協作(Collaboration) | 定義元素之間的相互作用 | |
元件(Component) | 描述物理系統的一部分 | |
活動類(Active Class) | 指物件有一個或多個程序或執行緒。活動類和類很相象,只是它的物件代表的元素的行為和其他元素是同時存在的 | |
節點(Node) | 定義為執行時存在的物理元素 |
2. 行為事物
行為事物指 UML 模型中的動態部分,如下表所示。
事物 | 解釋 | 用例 |
---|---|---|
互動(Interaction) | 包括一組元素之間的訊息交換 | |
狀態機(State Machine) | 由一系列物件的狀態組成 |
3. 分組事物
目前只有一種分組事物,即包。包純碎是概念上的,只存在於開發階段,結構事物、行為事物甚至分組事物都有可能放在一個包中,如下表所示。
事物 | 解釋 | 用例 |
---|---|---|
包(Package) | UML中唯一的組織機制 |
4. 註釋事物
註釋事物是解釋 UML 模型元素的部分,如下表所示。
事物 | 解釋 | 用例 |
---|---|---|
註釋(Note) | 用於解析說明 UML 元素 |
關於 UML 中的關係,我們在《UML類圖及類圖之間的關係》一節講解。
圖
UML2.0 一共有 13 種圖(UML1.5 定義了 9 種,UML2.0 增加了 4 種),分別是類圖、物件圖、構件圖、部署圖、活動圖、狀態圖、用例圖、時序圖、協作圖 9 種,以及包圖、組合結構圖、時間圖、互動概覽圖 4 種。
圖名稱 | 解釋 |
---|---|
類圖(Class Diagrams) | 用於定義系統中的類 |
物件圖(Object Diagrams) | 類圖的一個例項,描述了系統在具體時間點上所包含的物件及各個物件之間的關係 |
構件圖(Component Diagrams) | 一種特殊的 UML 圖,描述系統的靜態實現檢視 |
部署圖(Deployment Diagrams) | 定義系統中軟硬體的物理體系結構 |
活動圖(Activity Diagrams) | 用來描述滿足用例要求所要進行的活動及活動間的約束關係 |
狀態圖(State Chart Diagrams) | 用來描述類的物件的所有可能的狀態和時間發生時,狀態的轉移條件 |
用例圖(Usecase Diagrams) | 用來描述使用者的需求,從使用者的角度描述系統的功能,並指出各功能的執行者,強調誰在使用系統、系統為執行者完成哪些功能 |
時序圖(Sequence Diagrams) | 描述物件之間的互動順序,著重體現物件間訊息傳遞的時間順序,強調物件之間訊息的傳送順序,同時顯示物件之間的互動過程 |
協作圖(Collaboration Diagrams) | 描述物件之間的合作關係,更側重向用戶物件說明哪些物件有訊息的傳遞 |
包圖(Package Diagrams) | 對構成系統的模型元素進行分組整理的圖 |
組合結構圖(Composite Structure Diagrams) | 表示類或者構建內部結構的圖 |
時間圖(Timing Diagrams) | 用來顯示隨時間變化,一個或多個元素的值或狀態的更改,也顯示時間控制事件之間的互動及管理它們的時間和期限約束 |
互動概覽圖(Interaction Overview Diagrams) | 用活動圖來表示多個互動之間的控制關係的圖 |
一句話總結軟體設計七大原則
各種原則要求的側重點不同,下面我們分別用一句話歸納總結軟體設計模式的七大原則,如下表所示。
設計原則 | 一句話歸納 | 目的 |
---|---|---|
開閉原則 | 對擴充套件開放,對修改關閉 | 降低維護帶來的新風險 |
依賴倒置原則 | 高層不應該依賴低層,要面向介面程式設計 | 更利於程式碼結構的升級擴充套件 |
單一職責原則 | 一個類只幹一件事,實現類要單一 | 便於理解,提高程式碼的可讀性 |
介面隔離原則 | 一個介面只幹一件事,介面要精簡單一 | 功能解耦,高聚合、低耦合 |
迪米特法則 | 不該知道的不要知道,一個類應該保持對其它物件最少的瞭解,降低耦合度 | 只和朋友交流,不和陌生人說話,減少程式碼臃腫 |
里氏替換原則 | 不要破壞繼承體系,子類重寫方法功能發生改變,不應該影響父類方法的含義 | 防止繼承氾濫 |
合成複用原則 | 儘量使用組合或者聚合關係實現程式碼複用,少使用繼承 | 降低程式碼耦合 |
實際上,這些原則的目的只有一個:降低物件之間的耦合,增加程式的可複用性、可擴充套件性和可維護性。
建立型模式的特點和分類
建立型模式分為以下幾種。
- 單例(Singleton)模式:某個類只能生成一個例項,該類提供了一個全域性訪問點供外部獲取該例項,其拓展是有限多例模式。
- 原型(Prototype)模式:將一個物件作為原型,通過對其進行復制而克隆出多個和原型類似的新例項。
- 工廠方法(FactoryMethod)模式:定義一個用於建立產品的介面,由子類決定生產什麼產品。
- 抽象工廠(AbstractFactory)模式:提供一個建立產品族的介面,其每個子類可以生產一系列相關的產品。
- 建造者(Builder)模式:將一個複雜物件分解成多個相對簡單的部分,然後根據不同需要分別建立它們,最後構建成該複雜物件。
單例模式的應用場景
對於 Java 來說,單例模式可以保證在一個 JVM 中只存在單一例項。單例模式的應用場景主要有以下幾個方面。
- 需要頻繁建立的一些類,使用單例可以降低系統的記憶體壓力,減少 GC。
- 某類只要求生成一個物件的時候,如一個班中的班長、每個人的身份證號等。
- 某些類建立例項時佔用資源較多,或例項化耗時較長,且經常使用。
- 某類需要頻繁例項化,而建立的物件又頻繁被銷燬的時候,如多執行緒的執行緒池、網路連線池等。
- 頻繁訪問資料庫或檔案的物件。
- 對於一些控制硬體級別的操作,或者從系統上來講應當是單一控制邏輯的操作,如果有多個例項,則系統會完全亂套。
- 當物件需要被共享的場合。由於單例模式只允許建立一個物件,共享該物件可以節省記憶體,並加快物件訪問速度。如 Web 中的配置物件、資料庫的連線池等。
第 1 種:懶漢式單例
public class LazySingleton {
private static volatile LazySingleton instance = null; //保證 instance 在所有執行緒中同步
private LazySingleton() {
} //private 避免類在外部被例項化
public static synchronized LazySingleton getInstance() {
//getInstance 方法前加同步
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
第 2 種:餓漢式單例
public class HungrySingleton {
private static final HungrySingleton instance = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return instance;
}
}
原型模式(原型設計模式)詳解
原型模式的應用場景
原型模式通常適用於以下場景。
- 物件之間相同或相似,即只是個別的幾個屬性不同的時候。
- 建立物件成本較大,例如初始化時間長,佔用CPU太多,或者佔用網路資源太多等,需要優化資源。
- 建立一個物件需要繁瑣的資料準備或訪問許可權等,需要提高效能或者提高安全性。
- 系統中大量使用該類物件,且各個呼叫者都需要給它的屬性重新賦值。
在 Spring 中,原型模式應用的非常廣泛,例如 scope='prototype'、JSON.parseObject() 等都是原型模式的具體應用。
//具體原型類
class Realizetype implements Cloneable {
Realizetype() {
System.out.println("具體原型建立成功!");
}
public Object clone() throws CloneNotSupportedException {
System.out.println("具體原型複製成功!");
return (Realizetype) super.clone();
}
}
//原型模式的測試類
public class PrototypeTest {
public static void main(String[] args) throws CloneNotSupportedException {
Realizetype obj1 = new Realizetype();
Realizetype obj2 = (Realizetype) obj1.clone();
System.out.println("obj1==obj2?" + (obj1 == obj2));
}
}
簡單工廠模式
優點和缺點
優點:
- 工廠類包含必要的邏輯判斷,可以決定在什麼時候建立哪一個產品的例項。客戶端可以免除直接建立產品物件的職責,很方便的創建出相應的產品。工廠和產品的職責區分明確。
- 客戶端無需知道所建立具體產品的類名,只需知道引數即可。
- 也可以引入配置檔案,在不修改客戶端程式碼的情況下更換和新增新的具體產品類。
缺點:
- 簡單工廠模式的工廠類單一,負責所有產品的建立,職責過重,一旦異常,整個系統將受影響。且工廠類程式碼會非常臃腫,違背高聚合原則。
- 使用簡單工廠模式會增加系統中類的個數(引入新的工廠類),增加系統的複雜度和理解難度
- 系統擴充套件困難,一旦增加新產品不得不修改工廠邏輯,在產品型別較多時,可能造成邏輯過於複雜
- 簡單工廠模式使用了 static 工廠方法,造成工廠角色無法形成基於繼承的等級結構。
應用場景
對於產品種類相對較少的情況,考慮使用簡單工廠模式。使用簡單工廠模式的客戶端只需要傳入工廠類的引數,不需要關心如何建立物件的邏輯,可以很方便地建立所需產品。
public class Client {
public static void main(String[] args) {
}
//抽象產品
public interface Product {
void show();
}
//具體產品:ProductA
static class ConcreteProduct1 implements Product {
public void show() {
System.out.println("具體產品1顯示...");
}
}
//具體產品:ProductB
static class ConcreteProduct2 implements Product {
public void show() {
System.out.println("具體產品2顯示...");
}
}
final class Const {
static final int PRODUCT_A = 0;
static final int PRODUCT_B = 1;
static final int PRODUCT_C = 2;
}
static class SimpleFactory {
public static Product makeProduct(int kind) {
switch (kind) {
case Const.PRODUCT_A:
return new ConcreteProduct1();
case Const.PRODUCT_B:
return new ConcreteProduct2();
}
return null;
}
}
}
工廠方法模式(詳解版)
優點:
- 使用者只需要知道具體工廠的名稱就可得到所要的產品,無須知道產品的具體建立過程。
- 靈活性增強,對於新產品的建立,只需多寫一個相應的工廠類。
- 典型的解耦框架。高層模組只需要知道產品的抽象類,無須關心其他實現類,滿足迪米特法則、依賴倒置原則和里氏替換原則。
缺點:
- 類的個數容易過多,增加複雜度
- 增加了系統的抽象性和理解難度
- 抽象產品只能生產一種產品,此弊端可使用抽象工廠模式解決。
應用場景:
- 客戶只知道建立產品的工廠名,而不知道具體的產品名。如 TCL 電視工廠、海信電視工廠等。
- 建立物件的任務由多個具體子工廠中的某一個完成,而抽象工廠只提供建立產品的介面。
- 客戶不關心建立產品的細節,只關心產品的品牌
抽象工廠模式(詳解版)
interface AbstractFactory {
public Product1 newProduct1();
public Product2 newProduct2();
}
class ConcreteFactory1 implements AbstractFactory {
public Product1 newProduct1() {
System.out.println("具體工廠 1 生成-->具體產品 11...");
return new ConcreteProduct11();
}
public Product2 newProduct2() {
System.out.println("具體工廠 1 生成-->具體產品 21...");
return new ConcreteProduct21();
}
}