摘要:原型模式是創建模式的一種,其作用是提高創建效率,減少計算機資源開銷,與工廠模式類似的是,都屏蔽了對象實例化的過程概述原型模式是模式的一種,其特點就是通過克隆拷貝的方式來,節約創建成本和資源,被拷貝的對象模型就稱之為原型。
概述原型模式(Prototype Pattern)是創建模式的一種,其作用是提高創建效率,減少計算機資源開銷,與工廠模式類似的是,都屏蔽了對象實例化的過程...
原型模式是23GOF模式的一種,其特點就是通過克隆/拷貝的方式來,節約創建成本和資源,被拷貝的對象模型就稱之為原型。
舉例:在寫PPT的時候,大多情況下模板風格都是一致的,只是其中部分描述內容發生變化,這個時候你會選擇Ctrl+C/V還是新建一頁PPT一邊聽《從頭再來》一邊調整圖片和樣式?
JAVA中對原型模式提供了良好的支持,我們只需要實現Cloneable接口即可,它的目的就是將對象標記為可被復制
優點
簡化對象創建過程,通過拷貝的方式構建效率更高
可運行時指定動態創建的對象
缺點
需要實現 Cloneable接口,clone位于內部,不易擴展,容易違背開閉原則(程序擴展,不應該修改原有代碼)
默認的 clone 只是淺克隆,深度克隆需要額外編碼(比如:統一實現Cloneable接口,或者序列化方式,還有org.apache.commons:commons-lang3.SerializationUtils.java)
注意點
通過內存拷貝的方式構建出來的,會忽略構造函數限制
需要注意深拷貝和淺拷貝,默認Cloneable 是淺拷貝,只拷貝當前對象而不會拷貝引用對象,除非自己實現深拷貝
與單例模式沖突,clone是直接通過內存拷貝的方式,繞過構造方法
常用克隆不可變對象,如果你克隆的對象10個字段改9個還不如實例化算了
clone只是一個語法,非強制方法命名
很少多帶帶出現,常與工廠模式相伴
適用場景
常用在初始化步驟繁瑣,資源耗損嚴重的對象
案例案例一:淺拷貝
class Address { private String description; // 省略 Getter And Setter } public class Customer implements Cloneable { private int id; private String name; private Address address; private Listhobbies; // 省略 Getter And Setter @Override protected Customer clone() throws CloneNotSupportedException { return (Customer) super.clone(); } @Override public String toString() { return "Customer{" + "id=" + id + ", name="" + name + """ + ", address=" + address + ", hobbies=" + hobbies + ", customer-hashCode=" + this.hashCode() + ", address-hashCode=" + this.address.hashCode() + "}"; } } public class PrototypeDemo { public static void main(String[] args) throws CloneNotSupportedException { List hobbies = new ArrayList<>(); hobbies.add("籃球"); hobbies.add("足球"); Customer original = new Customer(1, "淘寶客戶-1", new Address("上海市"), hobbies); Customer cloned = original.clone(); System.out.println("http:////////////////////////////////////////////////// 修改前原始信息信息 - 開始 //////////////////////////////////////////////////"); System.out.println("原始信息:" + original.toString()); System.out.println("拷貝信息:" + cloned.toString()); System.out.println("http:////////////////////////////////////////////////// 修改前原始信息信息 - 結束 ////////////////////////////////////////////////// "); } }
分析: 從日志中可以發現,我們無需通過new Object()的方式去實例化對象,而是調用Object.clone()同樣可以,Customer的hashCode也發生了改變,由此可以推斷出它們的引用已經發生變化了,但是Address的hashCode一模一樣,前面說到過Cloneable是淺克隆的,并不會拷貝其它引用對象
////////////////////////////////////////////////// 修改前原始信息信息 - 開始 ////////////////////////////////////////////////// 原始信息:Customer{id=1, name="淘寶客戶-1", address=Address{description="上海市"}, hobbies=[籃球, 足球], customer-hashCode=460141958, address-hashCode=1163157884} 拷貝信息:Customer{id=1, name="淘寶客戶-1", address=Address{description="上海市"}, hobbies=[籃球, 足球], customer-hashCode=1956725890, address-hashCode=1163157884} ////////////////////////////////////////////////// 修改前原始信息信息 - 結束 //////////////////////////////////////////////////
案例二:淺拷貝
public class PrototypeDemo { public static void main(String[] args) throws CloneNotSupportedException { Listhobbies = new ArrayList<>(); hobbies.add("籃球"); hobbies.add("足球"); Customer original = new Customer(1, "淘寶客戶-1", new Address("上海市"), hobbies); Customer cloned = original.clone(); System.out.println("http:////////////////////////////////////////////////// 修改后原始信息信息 - 開始 //////////////////////////////////////////////////"); original.setName("淘寶客戶-2"); original.getHobbies().add("音樂"); System.out.println("原始信息:" + original.toString()); System.out.println("拷貝信息:" + cloned.toString()); System.out.println("http:////////////////////////////////////////////////// 修改后原始信息信息 - 結束 ////////////////////////////////////////////////// "); } }
分析: Customer本身的屬性值修改與原始對象并不沖突,它們都是各自一份,但集合類型修改后,克隆對象輸出的與原始對象如出一轍,不難發現Cloneable的淺克隆范圍只支持基本類型
////////////////////////////////////////////////// 修改后原始信息信息 - 開始 ////////////////////////////////////////////////// 原始信息:Customer{id=1, name="淘寶客戶-2", address=Address{description="上海市"}, hobbies=[籃球, 足球, 音樂], customer-hashCode=460141958, address-hashCode=1163157884} 拷貝信息:Customer{id=1, name="淘寶客戶-1", address=Address{description="上海市"}, hobbies=[籃球, 足球, 音樂], customer-hashCode=1956725890, address-hashCode=1163157884} ////////////////////////////////////////////////// 修改后原始信息信息 - 結束 //////////////////////////////////////////////////
案例三:深淺拷貝命名?
class Address implements Cloneable { // Address 也實現 Cloneable } @Override protected Address clone() throws CloneNotSupportedException { return (Address) super.clone(); } public class Customer implements Cloneable { // 重復代碼省略... protected Customer shallow() throws CloneNotSupportedException { return (Customer) super.clone(); } protected Customer deep() throws CloneNotSupportedException { Customer clone = (Customer) super.clone(); clone.setAddress(clone.getAddress().clone()); return clone; } } public class PrototypeDemo { public static void main(String[] args) throws CloneNotSupportedException { System.out.println("http:////////////////////////////////////////////////// 半深淺克隆 - 開始 //////////////////////////////////////////////////"); original.setAddress(new Address("北京市")); Customer shallow = original.shallow(); System.out.println("淺克隆:" + shallow.toString()); Customer deep = original.deep(); original.setAddress(new Address("天津市")); original.getHobbies().add("電影"); System.out.println("深克隆:" + deep.toString()); System.out.println("http:////////////////////////////////////////////////// 半深淺克隆 - 結束 ////////////////////////////////////////////////// "); } }
分析: 我們定義了兩個非clone的方法名,同樣可以做到克隆特性,此時從deep()中可以看到修改原始的Address并沒有影響到現有的克隆對象,這是因為內部通過硬編碼的方式控制的,雖然引用的Address對象發生改變,但是List
////////////////////////////////////////////////// 半深淺克隆 - 開始 ////////////////////////////////////////////////// 淺克隆:Customer{id=1, name="淘寶客戶-2", address=Address{description="北京市"}, hobbies=[籃球, 足球, 音樂], customer-hashCode=356573597, address-hashCode=1735600054} 深克隆:Customer{id=1, name="淘寶客戶-2", address=Address{description="北京市"}, hobbies=[籃球, 足球, 音樂, 電影], customer-hashCode=21685669, address-hashCode=2133927002} ////////////////////////////////////////////////// 半深淺克隆 - 結束 //////////////////////////////////////////////////
案例四:序列化實現深度克隆
class Address implements Cloneable,Serializable { private static final long serialVersionUID = 783202091017893997L; } public class Customer implements Cloneable, Serializable { private static final long serialVersionUID = 783202091017893997L; protected Customer deepCopy() throws Exception { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bos); out.writeObject(this); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream in = new ObjectInputStream(bis); return (Customer) in.readObject(); } } public class PrototypeDemo { public static void main(String[] args) throws CloneNotSupportedException { System.out.println("http:////////////////////////////////////////////////// 深淺克隆 - 開始 //////////////////////////////////////////////////"); Customer deepCopy = original.deepCopy(); original.setAddress(new Address("長沙市")); original.getHobbies().add("乒乓球"); System.out.println("深克?。? + deepCopy.toString()); System.out.println("http:////////////////////////////////////////////////// 深淺克隆 - 結束 ////////////////////////////////////////////////// "); } }
分析: 實現Serializable,通過序列化的方式與原始數據完全脫離關系,從而達到深度克隆效果,當然一般用SerializationUtils.clone(original)方式比我們自己寫的會更好
org.apache.commons commons-lang3 3.4
////////////////////////////////////////////////// 深淺克隆 - 開始 ////////////////////////////////////////////////// 深克?。篊ustomer{id=1, name="淘寶客戶-2", address=Address{description="天津市"}, hobbies=[籃球, 足球, 音樂, 電影], customer-hashCode=1020371697, address-hashCode=789451787} ////////////////////////////////////////////////// 深淺克隆 - 結束 //////////////////////////////////////////////////
案例五:如何配合工廠模式使用?
這個簡單問題就留給讀者思考吧,歡迎探討交流,三人行必有我師!!!總結
本章介紹了什么是原型模式以及使用它的優缺點,其模式在開發過程中幾乎用不上(反正我沒用到過,歡迎探討),但這并不能說明它是無用,存在即合理,設計模式不僅用作與JAVA軟件編程,更多情況下模式的作用是在某種程度下避免復雜的設計
- 說點什么全文代碼:https://gitee.com/battcn/design-pattern/tree/master/Chapter3/battcn-prototype
個人QQ:1837307557
battcn開源群(適合新手):391619659
微信公眾號:battcn(歡迎調戲)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/67912.html
摘要:設計模式的分類經典應用框架中常見的設計模式分為三類創建型模式對類的實例化過程的抽象。對象的結構模式是動態的。對象的行為模式則使用對象的聚合來分配行為。設計模式是個好東西,以后肯定還要進一步的學習,并且在項目中多實踐,提升自己的設計能力。 什么是設計模式? Christopher Alexander?說過:每一個模式描述了一個在我們周圍不斷重復發生的問題,以及該問題的解決方案的核心。這樣...
摘要:在中,除了幾種原始類型外,其余皆為對象,,既然對象如此重要,那就列舉一下在中如何創建對象通過構造函數創建對象實例對象字面量對象字面量是對象定義的一種簡寫形式,目的在于簡化創建包含大量屬性的對象的過程。 在Javascript中,除了幾種原始類型外,其余皆為對象(Object,Array ...),既然對象如此重要,那就列舉一下在Javascript中如何創建對象: 通過Object構造...
摘要:我們已經回答了的構造函數和原型都是誰的問題,現在牽扯出來一個,我們繼續檢查的構造函數是全局對象上屬性叫的對象的原型是個匿名函數,按照關于構造函數的約定,它應該是構造函數的屬性我們給這個對象起個名字,叫。 我不確定JavaScript語言是否應該被稱為Object-Oriented,因為Object Oriented是一組語言特性、編程模式、和設計與工程方法的籠統稱謂,沒有一個詳盡和大家...
閱讀 1017·2023-04-25 22:27
閱讀 872·2021-11-22 14:56
閱讀 984·2021-11-11 16:54
閱讀 1678·2019-08-30 15:54
閱讀 3500·2019-08-30 13:20
閱讀 1213·2019-08-30 10:55
閱讀 2080·2019-08-26 13:34
閱讀 3281·2019-08-26 11:53