👋hi,我不是一名外包公司的員工,也不會偷吃茶水間的零食,我的夢想是能寫高端CRUD
🔥 2025本人正在沉淀中… 博客更新速度++
👍 歡迎點贊、收藏、關注,跟上我的更新節奏
🎵 當你的天空突然下了大雨,那是我在為你炸烏云
文章目錄
- 一、入門
- 什么是原型模式?
- 為什么要有原型模式?
- 如何實現原型模式?
- 二、原型模式在框架源碼中的使用
- JDK 的 Object.clone()
- Java 集合框架中的 ArrayList.clone()
- Spring Framework 中的 Prototype Bean
- 三、總結
- 原型模式的優點
- 原型模式的缺點
- 原型模式的適用場景
一、入門
什么是原型模式?
原型模式(Prototype Pattern)是一種創建型設計模式,它通過復制現有對象來創建新對象,而不是通過實例化類。
原型模式的核心是克隆(Clone),即通過復制現有對象來創建新對象。Java 提供了 Cloneable 接口和 clone() 方法來實現對象的淺拷貝。
為什么要有原型模式?
- 對象創建成本高
- 問題:某些對象的創建過程可能非常復雜,涉及大量的計算或資源消耗。例如,數據庫連接、網絡請求、復雜的初始化過程等。
- 解決方案:通過原型模式,可以直接復制現有對象,避免重復進行高成本的初始化過程。
- 需要動態配置對象
- 問題:在某些情況下,對象的配置需要在運行時動態確定,而不是在編譯時確定。如果每次都需要重新創建和配置對象,會導致代碼復雜且難以維護。
- 解決方案:原型模式允許通過復制現有對象來創建新對象,并根據需要動態修改其配置。
- 避免子類化
- 問題:在某些情況下,通過繼承來擴展對象的功能會導致類層次結構復雜,難以維護。
- 解決方案:原型模式通過復制現有對象來創建新對象,而不是通過繼承來擴展功能,從而簡化了類層次結構。
- 保持對象狀態
- 問題:在某些情況下,需要創建與現有對象狀態相同的新對象。如果每次都手動復制對象的狀態,會導致代碼冗余且容易出錯。
- 解決方案:原型模式通過克隆現有對象來自動復制其狀態,減少了手動復制狀態的工作量。
如何實現原型模式?
原型模式由以下角色組成:
- 原型接口(Prototype Interface)
- 定義克隆方法的接口或抽象類。
- 在 Java 中,通常使用 Cloneable 接口來標記類支持克隆。
- 具體原型類(Concrete Prototype)
- 實現原型接口的具體類,負責實現克隆方法。
- 通常需要重寫 clone() 方法來定義具體的克隆邏輯。
- 客戶端(Client)
- 使用原型對象的類,通過調用原型對象的克隆方法來創建新對象。
【案例】打印身份證
假設你需要復印身份證,每次復印時,原始身份證的內容(姓名、身份證號、地址等)是固定的,但復印件的用途可能不同(如用于銀行開戶、辦理簽證等)。如果每次都重新填寫身份證信息,效率很低。
原型模式解決方案:
- 身份證原件(原型):包含固定的個人信息。
- 復印件(克隆對象):復制原件的內容,并根據用途添加額外信息(如用途備注)。
原型接口(Cloneable 是 Java 內置接口): Prototype
接口
interface Prototype extends Cloneable {Prototype clone();
}
具體原型類(Concrete Prototype) IDCard
類
class IDCard implements Prototype {private String name;private String idNumber;private String address;private String purpose; // 復印件的用途public IDCard(String name, String idNumber, String address) {this.name = name;this.idNumber = idNumber;this.address = address;}public void setPurpose(String purpose) {this.purpose = purpose;}@Overridepublic IDCard clone() {try {return (IDCard) super.clone(); // 調用 Object 的 clone() 方法} catch (CloneNotSupportedException e) {throw new RuntimeException(e);}}@Overridepublic String toString() {return "IDCard{" +"name='" + name + '\'' +", idNumber='" + idNumber + '\'' +", address='" + address + '\'' +", purpose='" + purpose + '\'' +'}';}
}
客戶端
class CopyMachine {public static void main(String[] args) {// 創建身份證原件IDCard originalIDCard = new IDCard("張三", "123456789012345678", "北京市朝陽區");// 復印身份證用于銀行開戶IDCard copyForBank = originalIDCard.clone();copyForBank.setPurpose("銀行開戶");// 復印身份證用于辦理簽證IDCard copyForVisa = originalIDCard.clone();copyForVisa.setPurpose("辦理簽證");// 打印結果System.out.println("原件: " + originalIDCard);System.out.println("銀行開戶復印件: " + copyForBank);System.out.println("辦理簽證復印件: " + copyForVisa);}
}
二、原型模式在框架源碼中的使用
JDK 的 Object.clone()
Java 的根類 Object 提供了 clone()
方法,它是原型模式的底層實現基礎。任何實現 Cloneable
接口的類都可以通過 clone()
方法創建新對象。
public class Object {protected native Object clone() throws CloneNotSupportedException;
}
使用約束
- 必須實現
Cloneable
接口:否則調用clone()
會拋出CloneNotSupportedException
。 - 默認是淺拷貝:需要深拷貝時,必須手動重寫
clone()
方法。
Java 集合框架中的 ArrayList.clone()
Java 的 ArrayList
類實現了 Cloneable
接口,其 clone()
方法通過淺拷貝快速復制一個新的列表。
ArrayList<String> original = new ArrayList<>();
original.add("A");
original.add("B");ArrayList<String> copy = (ArrayList<String>) original.clone();
copy.add("C");System.out.println(original); // 輸出 [A, B]
System.out.println(copy); // 輸出 [A, B, C]
源碼實現
public class ArrayList<E> implements Cloneable {// ...public Object clone() {try {ArrayList<?> v = (ArrayList<?>) super.clone();v.elementData = Arrays.copyOf(elementData, size);v.modCount = 0;return v;} catch (CloneNotSupportedException e) {throw new InternalError(e);}}
}
Spring Framework 中的 Prototype Bean
Spring 框架中的 Bean 作用域(Scope)之一是 prototype,它表示每次從容器中獲取 Bean 時都會創建一個新的實例。這本質上是原型模式的一種應用。
<!-- XML 配置 -->
<bean id="prototypeBean" class="com.example.PrototypeBean" scope="prototype"/>
// Java 代碼中獲取 Bean
ApplicationContext context = ...;
PrototypeBean bean1 = context.getBean(PrototypeBean.class);
PrototypeBean bean2 = context.getBean(PrototypeBean.class);// bean1 和 bean2 是兩個不同的實例
System.out.println(bean1 == bean2); // 輸出 false
設計思想
- 核心機制:Spring 容器內部維護一個原型 Bean 的模板,每次調用
getBean()
時,基于模板創建一個新對象(通過反射或 CGLIB 動態代理)。 - 優勢:避免單例 Bean 的線程安全問題,適用于需要獨立狀態的場景(如 HTTP 請求處理)。
三、總結
原型模式的優點
- 高效創建對象
- 通過復制現有對象來創建新對象,避免了重復的初始化過程,特別適用于創建成本較高的對象(如數據庫連接、復雜計算對象)。
- 例如,Spring 的 Prototype Bean 每次請求都會創建一個新實例,避免了單例 Bean 的線程安全問題。
- 動態配置對象
- 可以在運行時動態修改克隆對象的屬性,而不需要重新初始化。
- 例如,復制一個配置對象后,可以根據需要調整某些參數。
- 減少子類化
- 通過復制現有對象來創建新對象,而不是通過繼承來擴展功能,避免了類層次結構的復雜性。
- 例如,JavaScript 中的原型繼承就是通過復制原型對象來實現的。
- 標準化對象創建
- 提供統一的克隆接口(如
Cloneable
),使得對象創建過程更加標準化和可控。
- 提供統一的克隆接口(如
原型模式的缺點
- 深拷貝與淺拷貝問題
- 默認的
clone()
方法是淺拷貝,如果對象包含引用類型字段,克隆對象和原對象會共享這些字段,可能導致意外的副作用。 - 實現深拷貝需要額外的工作(如手動復制引用類型字段或使用序列化工具)。
- 默認的
- 實現復雜度
- 如果對象結構非常復雜(如包含嵌套對象、循環引用等),實現深拷貝可能會變得復雜且容易出錯。
- 破壞封裝性
clone()
方法是 Object 類的受保護方法,需要在子類中重寫并公開,這可能破壞類的封裝性。
- 性能問題
- 深拷貝(如基于序列化的拷貝)可能比較耗時,特別是在對象結構復雜或數據量大的情況下。
原型模式的適用場景
- 對象創建成本高
- 當對象的創建過程涉及復雜的計算、資源消耗(如數據庫連接、網絡請求)或耗時操作時,使用原型模式可以顯著提高性能。
- 例如,Spring 的 Prototype Bean 適用于需要獨立狀態的場景。
- 需要動態配置對象
- 當對象的配置需要在運行時動態確定時,可以通過克隆現有對象并修改其屬性來實現。
- 例如,復制一個配置模板并根據環境調整參數。
- 避免子類化
- 當需要通過擴展對象的功能來創建新對象,但又不想引入復雜的類層次結構時,可以使用原型模式。
- 例如,JavaScript 中的原型繼承。
- 需要保持對象狀態一致性
- 當需要創建與現有對象狀態相同的新對象時,可以通過克隆來確保狀態一致性。
- 例如,游戲中的敵人生成、緩存對象的復制。
- 框架中的對象管理
- 在框架中,原型模式常用于管理對象的生命周期和創建過程(如 Spring 的 Prototype Bean、Java 集合框架的
clone()
方法)。
- 在框架中,原型模式常用于管理對象的生命周期和創建過程(如 Spring 的 Prototype Bean、Java 集合框架的