原型模式(Prototype Pattern)。
定義:
用原型例項指定建立物件的種類,並且通過拷貝這些原型建立新的物件。
不通過new關鍵字來產生一個物件,而是通過物件複製來實現的模式就叫做原型模式。
原型模式的核心就是一個clone方法,通過該方法進行物件的拷貝,Java提供了一個Cloneable介面來標示這個物件是可拷貝的,為什麼說是“標示”呢?翻開JDK的幫助看看Cloneable是一個方法都沒有的,這個介面只是一個標記作用,在JVM中具有這個標記的物件才有可能被拷貝。
通用原始碼:
public class PrototypeClass implements Cloneable {
// 覆寫父類Object方法
@Override
public PrototypeClass clone() {
PrototypeClass prototypeClass = null;
try{
prototypeClass = (PrototypeClass) super.clone();
} catch(CloneNotSupportedException e) {
// 異常處理
}
return prototypeClass;
}
}
優點:
- 效能優良
原型模式是在記憶體二進位制流的拷貝,要比直接new一個物件效能好很多,特別是要在一個迴圈體內產生大量的物件時,原型模式可以更好地體現其優點。
- 逃避建構函式的約束
這既是它的優點也是缺點,直接在記憶體中拷貝,建構函式是不會執行的。優點就是減少了約束,缺點就是減少了約束,需要大家在實際應用時考慮。
使用場景:
- 資源優化場景
類初始化需要消化非常多的資源,這個資源包括資料、硬體資源等。
- 效能和安全要求的場景
通過new產生一個物件需要非常繁瑣的資料準備或訪問許可權,則可以使用原型模式。
- 一個物件多個修改者的場景
一個物件需要提供給其他物件訪問,而且各個呼叫者可能都需要修改其值時,可以考慮使用原型模式拷貝多個物件供呼叫者使用。
在實際專案中,原型模式很少單獨出現,一般是和工廠方法模式一起出現,通過clone的方法建立一個物件,然後由工廠方法提供給呼叫者。
注意事項:
- 建構函式不會被執行
Object類的clone方法的原理是從記憶體中(具體地說是堆記憶體)以二進位制流的方式進行拷貝,重新分配一個記憶體塊,那建構函式沒有被執行也是非常正常的了。
- 淺拷貝
Object類提供的方法clone只是拷貝本物件,其物件內部的陣列、引用物件等都不拷貝,還是指向原生物件的內部元素地址,這種拷貝就叫做淺拷貝。確實是非常淺,兩個物件共享了一個私有變數,你改我改大家都能改,這是一種非常不安全的方式,在實際專案中使用還是還是比較少的(當然,這也是一種“危機”環境的一種救命方式)。內部的陣列和引用物件才不拷貝,其他的原始型別如int、long、char燈都會被拷貝,但是對於String型別,Java就希望你把它認為是基本型別自,它是沒有clone方法的,處理機制也比較特殊,通過字串池(stringpool)在需要的時候才在記憶體中建立新的字串,讀者在使用的時候就把String當做基本類使用即可。
注意:使用原型模式時,引用的成員變數必須滿足兩個條件才不會被拷貝:一是類的成員變數,而不是方法內變數;二十必須是一個可變的引用物件,而不是一個原始型別或不可變物件。
- 深拷貝
你修改你的,我修改我的,不相互影響,這種拷貝就叫做深拷貝。深拷貝還有一種實現方法就是通過自己寫二進位制流來操作物件,然後實現物件的深拷貝。
注意:深拷貝和淺拷貝建議不要混合使用,特別是在涉及類的繼承時,父類有多個引用的情況就非常複雜,建議的方案是深拷貝和淺拷貝分開實現。
- clone與final兩個冤家
注意:要使用clone方法,類的成員變數上不要增加final關鍵字。
最佳實踐:
原型模式先產生出一個包含大量共有資訊的類,然後可以拷貝出副本,修正細節資訊,建立了一個完整地個性物件。
示例程式碼:
廣告信模板程式碼
public class AdvTemplate {
// 廣告信名稱
private String advSubject = "XX銀行國慶信用卡抽獎活動";
// 廣告信內容
private String advContext = "國慶抽獎活動通知:只要刷卡就送你一百萬!...";
// 取得廣告信的名稱
public String getAdvSubject() {
return this.advSubject();
}
// 取得廣告信的內容
public String getAdvContext() {
return this.advContext;
}
}
// 郵件類
public class Mail implements Cloneable {
// 收件人
private String receiver;
// 郵件名稱
private String subject;
// 稱謂
private String appellation;
// 郵件內容
private String context;
// 郵件的尾部,一般都是加上“XXX版權所有”等資訊
private String tail;
// 建構函式
public Mail(AdvTemplate advTemplate) {
this.context = advTemplate.getAdvContext();
this.subject = advTemplate.getAdvSubject();
}
@Override
public Mail clone() {
Mail mail = null;
try {
mail = (Mail) super.clone();
} catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return mail;
}
// 以下為getter/setter方法,省略
}
場景類
public class Client {
// 傳送賬單的數量,這個值是從資料庫中獲得
private static int MAX_COUNT = 6;
public static void main(String[] args) {
// 模擬傳送郵件
int i = 0;
// 把模板定義出來,這個是從資料中獲得
Mail mail = new Mail(new AdvTemplate());
mail.setTail("XX銀行版權所有");
while(i < MAX_COUNT) {
// 以下是每封郵件不同的地方
Mail cloneMail = mail.clone();
cloneMail.setAppelation(getRandString(5) + "先生(女士)");
cloneMail.setReceiver(getRandString(8) + ".com");
// 然後傳送郵件
sendMail(cloneMail);
i++;
}
}
// 傳送郵件
public static void sendMail(Mail mail) {
System.out.println("標題:" + mail.getSubject() + "\t收件人: " + mail.getReceiver() + "\t...傳送成功!");
}
// 獲得指定長度的隨機字串
public static String getRandString(int maxLength) {
String source = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
StringBuffer sb = new StringBuffer();
Random rand = new Random();
for(int i = 0; i < maxLength; i ++) {
sb.append(source.charAt(rand.nextInt(source.length())));
}
return sb.toString();
}
}
建構函式不會被執行
簡單的可拷貝物件
public class Thing implements Cloneable {
public Thing() {
System.out.println("建構函式被執行了...");
}
@Override
public Thing clone() {
Thing thing = null;
try{
thing = (Thing ) super.clone();
} catch(CloneNotSupportedException e) {
// 異常處理
}
return thing;
}
}
簡單的場景類
public class Client {
public static void main(String[] args) {
// 產生一個物件
Thing thing = new Thing();
// 拷貝一個物件
Thing cloneThing = thing.clone();
}
}
淺拷貝
public class Thing implements Cloneable {
// 定義一個私有變數
private ArrayList<String> arrayList = new ArrayList<String>();
@Override
public Thing clone() {
Thing thing = null;
try{
thing = (Thing ) super.clone();
} catch(CloneNotSupportedException e) {
// 異常處理
}
return thing;
}
// 設定arrayList的值
public void setValue(String value) {
this.arrayList.add(value);
}
// 取得arrayList的值
public ArrayList<String getValue() {
return this.arrayLIst;
}
}
淺拷貝測試
public class Client {
public static void main(String[] args) {
// 產生一個物件
Thing thing = new Thing();
// 設定一個值
thing.setValue("張三");
//拷貝一個物件
Thing cloneThing = thing.clone();
cloneThing.setValue("李四");
System.out.println(thing.getValue());
}
}
深拷貝
public class Thing implements Cloneable {
// 定義一個私有變數
private ArrayList<String> arrayList = new ArrayList<String>();
@Override
public Thing clone() {
Thing thing = null;
try{
thing = (Thing ) super.clone();
thing.arrayList = (ArrayLIst<String>) this. arrayList.clone();
} catch(CloneNotSupportedException e) {
// 異常處理
}
return thing;
}
// 設定arrayList的值
public void setValue(String value) {
this.arrayList.add(value);
}
// 取得arrayList的值
public ArrayList<String getValue() {
return this.arrayLIst;
}
}
相關文章
- 設計模式--原型模式(Prototype Pattern)設計模式原型
- C#設計模式-原型模式(Prototype Pattern)C#設計模式原型
- JAVA設計模式之 原型模式【Prototype Pattern】Java設計模式原型
- 原型模式(Prototype)原型模式
- 我所理解的設計模式(C++實現)——原型模式(Prototype Pattern)設計模式C++原型
- 設計模式-原型模式(Prototype)設計模式原型
- 建立模式 02-Prototype(原型)模式原型
- 設計模式之-原型模式-Prototype設計模式原型
- 3.4 PROTOTYPE(原型) — 物件建立型模式原型物件模式
- 《設計模式 》 - 4. 原型模式( Prototype )設計模式原型
- 設計模式--原型模式Prototype(建立型)設計模式原型
- 談談設計模式~原型模式(Prototype)設計模式原型
- 設計模式的征途—5.原型(Prototype)模式設計模式原型
- Scala 與設計模式(三):Prototype 原型模式設計模式原型
- C#設計模式系列:原型模式(Prototype)C#設計模式原型
- JavaScript prototype 原型JavaScript原型
- JavaScript:原型(prototype)JavaScript原型
- JavaScript prototype原型用法JavaScript原型
- 一天一個設計模式(四) – 原型模式(Prototype)設計模式原型
- 一天一個設計模式(四) - 原型模式(Prototype)設計模式原型
- Javascript玩轉Prototype(一)——先談C#原型模式JavaScriptC#原型模式
- Javascript篇之Prototype的原型JavaScript原型
- 理解js的 prototype原型物件JS原型物件
- javascript基礎(原型(prototype))(十七)JavaScript原型
- 設計模式-原型模式(Prototype)【重點:淺複製與深複製】設計模式原型
- 深入理解javascript原型和閉包(3)——prototype原型JavaScript原型
- JS 系列二:深入 constructor、prototype、__proto__、[[Prototype]] 及 原型鏈JSStruct原型
- 原型模式(prototype)優缺點和一些疑惑請大家指教原型模式
- JS原型鏈、prototype、__proto__、原型鏈繼承詳解JS原型繼承
- 原型和原型鏈 prototype和proto的區別詳情原型
- 全面瞭解 Javascript Prototype Chain 原型鏈JavaScriptAI原型
- 請教prototype模式!模式
- 設計模式之策略模式和狀態模式(strategy pattern & state pattern)設計模式
- 策略模式【Strategy Pattern】模式
- 代理模式(Proxy Pattern)模式
- 設計模式之Prototype設計模式
- 一張圖徹底KO原型鏈(prototype,__proto__)原型
- js 原型鏈繼承__proto__、prototype簡析JS原型繼承