重溫23種設計模式(11):原型模式
來源:mikechen的網際網路架構
為什麼要學設計模式?設計模式有哪些優點?
提升檢視框架原始碼能力 提升對複雜業務的程式碼設計能力以及 code 能力 為今後的面試以及進階之路夯實基礎
今天我們要講的是設計模式中的原型模式(Prototype Pattern)。
原型模式提供了建立物件的最佳方式,主要解決物件複製的問題,適用於建立複雜物件圖、實現物件的快照和恢復等場景中。
原型模式透過克隆現有物件來建立新物件,避免了頻繁的物件例項化過程,屬於建立型模式。
簡單理解,就是定義一個原型物件作為建立其他物件的基礎,透過克隆原型物件,我們可以建立多個具有相同屬性和行為的新物件。
例如:
孫悟空有個十分牛逼的絕活兒,叫分身術,可以幻化出多個相同的孫悟空。
這個幻化出的新的分身,和設計模式中的原型模式是相似的。
原型模式中的 3 個重要角色:
客戶端(Client):使用具體原型類中的 clone() 方法,來複制新的物件。
抽象原型(Prototype):可以是抽象類或介面,規定了具體原型物件必須實現的 clone() 方法。
具體原型類(ConcretePrototype):實現抽象原型類的 clone() 方法,它是可被複制的物件。
Prototype 通常不用自己定義,因為複製這個操作十分常用。
在 Java 中,提供了Cloneable 介面來支援複製操作,它就是原型模式中的 Prototype 。
當然了,原型模式也未必非得去實現 Cloneable 介面,也有其他的實現方式。
原型模式有兩種複製方式:淺複製、深複製。
1)淺複製
建立一個新物件,新物件的屬性和原來物件完全相同,對於非基本型別屬性,仍指向原有屬性所指向的物件的記憶體地址。
簡單理解,就是只複製所考慮的物件,而不復制它所引用的物件。
Object 類提供的方法 clone ,只是複製本物件 , 其物件內部的陣列、引用物件等都不複製。
2)深複製
建立一個新物件,屬性中引用的其他物件也會被克隆,不再指向原有物件地址。
簡單理解,就是複製一切,把要複製的物件所引用的物件都複製了一遍。
相比於淺複製,深複製速度慢並且開銷大,但是複製前後兩個物件互不影響。
原始碼示例:
將名片複製到自己的名片庫中。
我們先實現名片類。
具體的原型類:
public class BusinessCard implements Cloneable { private String name; private String company; public BusinessCard(){ System.out.println("執行建構函式BusinessCard"); } public void setName(String name) { this.name = name; } public void setCompany(String company) { this.company = company; } @Override public BusinessCard clone() { BusinessCard businessCard = null; try { businessCard = (BusinessCard) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return businessCard; } public void show() { System.out.println("name:" + name); System.out.println("company:" + company); }}
BusinessCard 類實現了 Cloneable 介面,它是一個標識介面,表示這個物件是可複製的。
只要重寫 clone 方法,就可以實現複製。如果實現了 Cloneable 介面、卻沒有重寫 clone 方法,就會報錯。
需要注意的是:
clone 方法不是在 Cloneable 介面中定義的(Cloneable 介面中沒有定義任何方法),而是在 Object 中定義的。
客戶端呼叫:
public class Client {
public static void main(String[] args) {
BusinessCard businessCard = new BusinessCard();
businessCard.setName("錢三");
businessCard.setCompany("阿里");
//複製名片
BusinessCard cloneCard1 = businessCard.clone();
cloneCard1.setName("趙四");
cloneCard1.setCompany("百度");
BusinessCard cloneCard2 = businessCard.clone();
cloneCard2.setName("孫五");
cloneCard2.setCompany("騰訊");
businessCard.show();
cloneCard1.show();
cloneCard2.show();
}
}
除了第一個名片,其他兩個名片都是透過 clone 方法得到的。
但是,clone 方法並不會執行 cloneCard1 和 cloneCard2 的建構函式。
執行結果:
執行建構函式 BusinessCardname:錢三company:阿里name:趙四company:百度name:孫五company:騰訊
1)實現淺複製
上述的例子中,BusinessCard 的欄位都是 String 型別的,如果欄位是引用的型別的,會出現什麼情況呢?
public class DeepBusinessCard implements Cloneable {
private String name;
private Company company = new Company();
public void setName(String name) {
this.name = name;
}
public void setCompany(String name, String address) {
this.company.setName(name);
this.company.setAddress(address);
}
@Override
public DeepBusinessCard clone() {
DeepBusinessCard businessCard = null;
try {
businessCard = (DeepBusinessCard) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return businessCard;
}
public void show() {
System.out.println("name:" + name);
System.out.println("company:" + company.getName() + "-address-" + company.getAddress());
}
}
我們定義了 DeepBusinessCard 類,它的欄位 company 是引用型別的。
Company 類:
public class Company {
private String name;
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
在客戶端使用 DeepBusinessCard :
public class Client {
public static void main(String[] args) {
DeepBusinessCard businessCard=new DeepBusinessCard();
businessCard.setName("錢三");
businessCard.setCompany("阿里","北京望京");
DeepBusinessCard cloneCard1=businessCard.clone();
cloneCard1.setName("趙四");
cloneCard1.setCompany("百度","北京西二旗");
DeepBusinessCard cloneCard2=businessCard.clone();
cloneCard2.setName("孫五");
cloneCard2.setCompany("騰訊","北京中關村");
businessCard.show();
cloneCard1.show();
cloneCard2.show();
}
}
執行結果:
name:錢三company:騰訊-address-北京中關村name:趙四company:騰訊-address-北京中關村name:孫五company:騰訊-address-北京中關村
Object 類提供的 clone 方法,不會複製物件中的內部陣列和引用物件,導致它們仍舊指向原來物件的內部元素地址。
因此,company 欄位為最後設定的”騰訊”、”北京中關村”。
這種複製方式,我們就稱為淺複製。
company 欄位是引用型別,businessCard 被複製後,company 欄位仍舊指向原來的 businessCard 物件的 company 欄位的地址。
我們每次設定 company 欄位,都會覆蓋上一次設定的值,最終留下的就是最後一次設定的值:騰訊、北京中關村。
淺複製引用關係:
有多個物件可以修改 company ,顯然,這樣的引用關係是不符合需求的。
為了解決這個問題,我們引入了深複製模式。
修改引用關係為深複製:
複製 businessCard 物件的同時,也將它內部的引用物件 company 進行複製,使得每個複製的物件之間無任何關聯,都指向了自身對應的 company。
這種複製方式,我們就稱為深複製。
深複製模式把要複製的物件所引用的物件都複製了一遍。
2)實現深複製
首先需要修改 Company 類:
public class Company implements Cloneable{ private String name; private String address; ... public Company clone(){ Company company=null; try { company= (Company) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return company; }}
為了實現 Company 類能被複製,Company 類也需要實現 Cloneable 介面、並且覆寫 clone 方法。
接著,修改 DeepBusinessCard 的 clone 方法:
public class DeepBusinessCard implements Cloneable {
private String name;
private Company company = new Company();
...
@Override
public DeepBusinessCard clone() {
DeepBusinessCard businessCard = null;
try {
businessCard = (DeepBusinessCard) super.clone();
businessCard.company = this.company.clone();//1
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return businessCard;
}
...
}
註釋 1 增加了對 company 欄位的複製處理,最後在客戶端呼叫。
輸出結果:
name:錢三company:阿里-address-北京望京name:趙四company:百度-address-北京西二旗name:孫五company:騰訊-address-北京中關村
可見,採用深複製模式修改一個物件的引用型別的成員時,不會再影響另外物件的該成員。
優點:
克隆物件時,對客戶端隱藏實現細節,避免了程式碼耦合。
逃避建構函式的約束。
可以動態增加或減少產品類。
原型模式提供了簡化的建立結構。
缺點:
必須實現 Cloneable 介面;
clone 方法位於類的內部,當對已有類進行改造的時候,需要修改程式碼,違背了開閉原則。
在實現深複製時,需要編寫較為複雜的程式碼。
不管是複雜還是簡單的物件,只要存在物件複製的場景,都適合使用原型模式。
類的初始化需要耗費較多的資源時。
透過 new 產生一個物件、需要非常繁瑣的資料準備或訪問許可權時。
一個物件需要提供給其他物件訪問,而且各個呼叫者可能都需要修改其值時。
工廠方法模式是一種建立型設計模式,它提供了一種在不指定具體類的情況下建立物件的方法。
關於工廠方法模式,看這篇:【重溫23種設計模式】之工廠方法模式
原型模式與工廠方法模式的主要區別是:物件建立的方式。
原型模式透過複製現有的物件來建立新物件,適用於以下場景:
需要建立大量具有相似屬性的物件。
物件的建立成本很高,例如需要執行復雜的初始化邏輯。
需要在執行時動態地建立和修改物件。
工廠方法模式透過呼叫工廠方法來建立新物件,適用於以下場景:
需要建立具有不同型別或不同實現的物件。
物件建立邏輯較為複雜,不適合在客戶端程式碼中直接例項化。
需要將物件建立過程與客戶端程式碼解耦。
單例模式是一種建立型設計模式,它確保一個類只有一個例項,並提供一個全域性訪問點。
關於單例模式,看這篇:8大單例模式實現方式總結
原型模式與單例模式的主要區別在於它們的目的。
原型模式用於透過複製現有物件來建立新物件,適用於以下場景:
需要建立大量具有相似屬性的物件。
物件的建立成本很高,例如需要執行復雜的初始化邏輯。
需要在執行時動態地建立和修改物件。
單例模式用於確保一個類只有一個例項,適用於以下場景:
需要確保一個類只有一個例項。
需要全域性訪問點以方便地訪問該例項。
需要全域性共享資源或配置資訊。
透過本文,我們瞭解並掌握了原型模式的概念、原理、應用場景、優缺點、實現方式等。
原型模式是一種強大而靈活的設計模式,它可以簡化物件的建立過程、提高效能,具備更好的可維護性。
在實現原型模式時,需要根據具體需求選擇使用深複製或淺複製。原型模式適用於在處理物件建立、動態修改物件結構、高效能快取等場景中。
以上,就是關於原型模式的全部介紹。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024922/viewspace-2996287/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 【重溫23種設計模式】之工廠方法模式設計模式
- 23種設計模式(八)-原型設計模式設計模式原型
- 23種設計模式-原型模式(3)設計模式原型
- java23種設計模式——四、原型模式Java設計模式原型
- java中23種設計模式--原型模式(Portotype)Java設計模式原型
- 23種設計模式設計模式
- 23種設計模式(二)---策略設計模式設計模式
- 23種設計模式(四)-代理模式設計模式
- 23種設計模式(七)-狀態設計模式設計模式
- 23種設計模式(四)- 模板方法設計模式設計模式
- 【23種設計模式】外觀模式(十)設計模式
- 23種設計模式:03裝飾模式設計模式
- 23種設計模式之組合模式設計模式
- 23種設計模式(抽象工廠模式)設計模式抽象
- 23種設計模式(六)-責任鏈設計模式設計模式
- GOF23--23種設計模式(一)Go設計模式
- 23種設計模式簡介設計模式
- 23種軟體設計模式設計模式
- 23種設計模式之介面卡模式設計模式
- 23種設計模式之直譯器模式設計模式
- 23種設計模式(三)--裝飾器模式設計模式
- 23種設計模式之——動態代理模式設計模式
- 【圖解設計模式系列】23句話總結23種設計模式圖解設計模式
- 重學 Java 設計模式:實戰原型模式Java設計模式原型
- 【重溫設計模式】之003工廠方法模式設計模式
- 淺談23種設計模式之單例設計模式設計模式單例
- 23種設計模式(一)---簡單工廠設計模式設計模式
- java23種設計模式-門面模式(外觀模式)Java設計模式
- java23種設計模式——八、組合模式Java設計模式
- 23種設計模式之代理模式(靜態代理)設計模式
- java23種設計模式——五、建造者模式Java設計模式
- java23種設計模式—— 二、單例模式Java設計模式單例
- java23種設計模式——三、工廠模式Java設計模式
- 實踐GoF的23種設計模式:命令模式Go設計模式
- 設計模式(原型模式)設計模式原型
- 設計模式:原型模式設計模式原型
- 設計模式-原型模式設計模式原型
- 設計模式 —— 原型模式設計模式原型