定義:
原型模式(Prototype Pattern)是一種創建型設計模式,它用于創建重復的對象,同時保持性能。這種模式的核心思想是通過復制一個已存在的實例來創建新的實例,而不是新建實例并對其進行初始化。原型模式適用于創建復雜對象的情況,特別是當對象創建的成本比較高時,如需要進行繁瑣的資源消耗型操作(例如,數據庫或文件的讀取操作)。
原型模式通常涉及以下幾個角色:
- 原型(Prototype):
- 定義用于復制現有對象以生成新對象的接口。
- 具體原型(Concrete Prototype):
- 實現原型接口的類,并提供復制自身的方法。這通常通過實現一個克隆方法(如Java中的
clone()
方法)來完成。
- 實現原型接口的類,并提供復制自身的方法。這通常通過實現一個克隆方法(如Java中的
- 客戶(Client):
- 創建新的對象時,客戶端使用原型實例提供的克隆方法來獲取新對象的副本,而不是直接通過
new
關鍵字創建。
- 創建新的對象時,客戶端使用原型實例提供的克隆方法來獲取新對象的副本,而不是直接通過
解決的問題:
- 高成本的對象創建:
- 當創建一個對象的成本很高時,因為它需要進行復雜的初始化,如從數據庫讀取數據或進行復雜的計算。原型模式通過復制現有對象來避免這種高成本的創建過程。
- 避免復雜的構造過程:
- 在某些情況下,對象的創建過程可能涉及多個步驟和要求,使用原型模式可以通過直接復制一個已經創建好的實例來簡化創建過程。
- 動態添加或刪除對象:
- 在運行時動態地添加或刪除具有特定配置的對象時,原型模式提供了一種方便的方法來復制配置相同的實例。
- 對象的解耦:
- 有時,系統需要獨立于其要創建對象的類。原型模式允許你復制一個對象,而不需要依賴于它們的具體類。
- 優化性能和內存:
- 使用原型模式可以減少系統的整體資源消耗,因為復制通常比創建全新實例更輕量級。
使用場景:
- 性能敏感的對象創建:
- 當對象的創建過程涉及昂貴的數據庫操作、文件讀取、復雜計算或網絡調用等,而復制現有對象的成本相對較低時。
- 避免復雜的初始化步驟:
- 如果一個對象的初始化過程非常復雜,如設置多個字段和依賴,使用原型模式可以通過克隆一個已經初始化的實例來避免這些復雜性。
- 類不容易獲取或無法預知:
- 當需要實例化的類在運行時才確定,或者類的實例化過程隱藏在一些我們無法訪問的API后面時。
- 動態加載或生成對象:
- 在需要根據當前環境或狀態動態生成對象的場景中,可以通過復制預先存儲的原型來實現。
- 大量相似對象的場景:
- 當系統需要大量相似對象時,使用原型模式可以避免類初始化時的重復工作。
- 實現對象的解耦:
- 當需要解耦系統中的對象創建和使用時,原型模式允許用戶無需知道對象的具體類型就能創建新實例。
示例代碼 1 - 淺拷貝實現:
public class ShallowPrototype implements Cloneable {private String name;public ShallowPrototype(String name) {this.name = name;}// getter和setter@Overridepublic ShallowPrototype clone() throws CloneNotSupportedException {return (ShallowPrototype) super.clone();}
}
示例代碼 2 - 深拷貝實現:
public class DeepPrototype implements Cloneable {private String name;private SomeComplexObject complexObject;public DeepPrototype(String name, SomeComplexObject complexObject) {this.name = name;this.complexObject = complexObject;}// getter和setter@Overridepublic DeepPrototype clone() throws CloneNotSupportedException {DeepPrototype cloned = (DeepPrototype) super.clone();cloned.complexObject = new SomeComplexObject(this.complexObject); // 創建新的復雜對象實例return cloned;}
}class SomeComplexObject {private String data;public SomeComplexObject(SomeComplexObject obj) {this.data = obj.data;}// getter和setter
}
主要符合的設計原則:
- 開閉原則(Open-Closed Principle):
- 原型模式支持開閉原則。一旦原型對象被創建并實現了克隆(Clone)方法,你可以通過克隆現有對象來添加新的對象實例,而無需修改現有的代碼。
- 單一職責原則(Single Responsibility Principle):
- 原型模式允許將對象創建和業務邏輯分離,使得每個類專注于單一的職責。原型對象專注于如何創建和復制自身的實例。
- 里氏替換原則(Liskov Substitution Principle):
- 在原型模式中,任何繼承自原型的新對象都應當能替代原型對象。這符合里氏替換原則,即程序中的對象應該能夠被其子類對象所替換,而程序的邏輯不受影響。
原型模式主要通過實現開閉原則和單一職責原則來提高代碼的可維護性和可擴展性。通過克隆方法,它允許在運行時動態地創建對象,提供了創建對象的靈活方式,同時避免了復雜的構造過程。
在JDK中的應用:
java.lang.Object
的clone()
方法:- 在Java中,所有類都繼承自
java.lang.Object
。Object
類提供了一個clone()
方法,可以用來復制對象。盡管這個方法默認是淺復制,但它提供了實現深復制的基礎。
- 在Java中,所有類都繼承自
- Java集合框架:
- 許多Java集合類(如
ArrayList
,HashSet
,HashMap
等)實現了Cloneable
接口,提供了clone()
方法來創建集合的副本。這些集合類的克隆方法通常提供了集合內容的淺復制。
- 許多Java集合類(如
- 日期和時間對象:
- 諸如
java.util.Date
這樣的日期和時間相關類也實現了Cloneable
接口,允許通過克隆方法來創建日期對象的精確副本。
- 諸如
在Spring中的應用:
- Bean的原型作用域:
- 在Spring框架中,Bean的作用域默認是單例(singleton),但可以配置為原型(prototype)。當一個Bean被定義為原型作用域時,每次通過容器請求這個Bean時,Spring容器都會創建一個新的Bean實例,而不是返回一個共享的單例實例。這正是原型模式的應用,即每次需要時都創建一個新的對象副本。
- 解決單例Bean的狀態問題:
- 在某些情況下,如果單例Bean包含了可變的數據字段,那么在并發環境下可能會出現數據安全問題。通過將這樣的Bean定義為原型作用域,可以為每個請求創建一個新的實例,從而避免了狀態共享的問題。
雖然Spring中原型作用域的應用并不廣泛,但在需要獨立狀態或避免共享狀態的場景中,原型模式提供了一種有效的解決方案。需要注意的是,與單例Bean相比,原型Bean的生命周期管理、依賴注入和銷毀需要更多地由開發者來控制。