摘要:缺點(diǎn)首先要記住原型模式的拷貝時(shí)不會(huì)執(zhí)行構(gòu)造函數(shù)的。源碼地址原型模式參考慕課網(wǎng)設(shè)計(jì)模式精講設(shè)計(jì)模式之原型模式原型模式示例六原型模式破壞單例模式
0x01.定義與類型
定義:指原型實(shí)例指定創(chuàng)建對(duì)象的種類,并且通過拷貝這些原型創(chuàng)建新的對(duì)象
特點(diǎn):不需要知道任何創(chuàng)建的細(xì)節(jié),不調(diào)用構(gòu)造函數(shù)
類型:創(chuàng)建型
UML
原型模式主要用于對(duì)象的復(fù)制,它的核心是就是類圖中的原型類Prototype。Prototype類需要具備以下兩個(gè)條件:
實(shí)現(xiàn)Cloneable接口。在java語言有一個(gè)Cloneable接口,它的作用只有一個(gè),就是在運(yùn)行時(shí)通知虛擬機(jī)可以安全地在實(shí)現(xiàn)了此接口的類上使用clone方法。在java虛擬機(jī)中,只有實(shí)現(xiàn)了這個(gè)接口的類才可以被拷貝,否則在運(yùn)行時(shí)會(huì)拋出CloneNotSupportedException異常。
重寫Object類中的clone方法。Java中,所有類的父類都是Object類,Object類中有一個(gè)clone方法,作用是返回對(duì)象的一個(gè)拷貝,但是其作用域protected類型的,一般的類無法調(diào)用,因此Prototype類需要將clone方法的作用域修改為public類型。
Java實(shí)現(xiàn)
/** * 原型模式 */ public class Prototype implements Cloneable { private String name; @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } public String getName() { return name; } public void setName(String name) { this.name = name; } } /** * 測試與應(yīng)用類 */ public class Test { public static void main(String[] args) throws CloneNotSupportedException { Prototype prototype = new Prototype(); prototype.setName("K.O"); Listnames = new ArrayList<>(); names.add("K.O"); prototype.setNames(names); for (int i = 0; i < 5; i ++) { Prototype p = (Prototype) prototype.clone(); p.setName("sigma"); p.getNames().add("sigma"); System.out.println(p.toString()); System.out.println(p.getName()); System.out.println(p.getNames().size()); } System.out.println(prototype.toString()); System.out.println(prototype.getName()); System.out.println(prototype.getNames().size()); } }
測試輸出結(jié)果
org.ko.prototype.basic.Prototype@1540e19d sigma 2 org.ko.prototype.basic.Prototype@677327b6 sigma 3 org.ko.prototype.basic.Prototype@14ae5a5 sigma 4 org.ko.prototype.basic.Prototype@7f31245a sigma 5 org.ko.prototype.basic.Prototype@6d6f6e28 sigma 6 org.ko.prototype.basic.Prototype@135fbaa4 K.O 6
可以看出,輸出結(jié)果中對(duì)象的地址不同(是重新創(chuàng)建的)
修改基本類型時(shí),并不能影響基礎(chǔ)類,而引用對(duì)象只是指向的基礎(chǔ)類的屬性。
這里有個(gè)問題叫深拷貝,淺拷貝,后續(xù)會(huì)介紹!
原型模式的各個(gè)元素,原型模式比較簡單,元素比較少
原型接口:適用原型模式要實(shí)現(xiàn)原型接口,重寫里面的 clone()方法
原型類:具體產(chǎn)品的實(shí)現(xiàn)
0x02.使用場景類初始化消耗較多資源。
new產(chǎn)生的一個(gè)對(duì)象需要非常繁瑣的過程(數(shù)據(jù)準(zhǔn)備、訪問權(quán)限等)。
構(gòu)造函數(shù)比較復(fù)雜。
循環(huán)體中生產(chǎn)大量對(duì)象時(shí)。
0x03.優(yōu)點(diǎn)原型模式性能比直接new一個(gè)對(duì)象性能高,是在內(nèi)存中二進(jìn)制流的拷貝,要比直接new一個(gè)對(duì)象性能好很多,特別是要在一個(gè)循環(huán)體內(nèi)產(chǎn)生大量對(duì)象時(shí),原型模式可能更好的體現(xiàn)其優(yōu)點(diǎn)。
還可以簡化創(chuàng)建過程。
還有一個(gè)重要的用途就是保護(hù)性拷貝,也就是對(duì)某個(gè)對(duì)象對(duì)外可能是只讀的,為了防止外部對(duì)這個(gè)只讀對(duì)象的修改,通??梢酝ㄟ^返回一個(gè)對(duì)象拷貝的形式實(shí)現(xiàn)只讀的限制。
0x04.缺點(diǎn)首先要記住原型模式的拷貝時(shí)不會(huì)執(zhí)行構(gòu)造函數(shù)的。
clone并不一定比new一個(gè)對(duì)象快,只有當(dāng)new對(duì)象比較耗時(shí)時(shí),才考慮使用原型模式。
必須配備克隆方法。
對(duì)克隆復(fù)雜對(duì)象或?qū)寺〕龅膶?duì)象進(jìn)行復(fù)雜改造時(shí),容易引入風(fēng)險(xiǎn)。
深拷貝、淺拷貝要運(yùn)用得當(dāng)。
要使用clone方法,類的成員變量上不要增加final關(guān)鍵字,final類型是不允許重賦值的。
0x05.樣例實(shí)現(xiàn)使用原型模式實(shí)現(xiàn)發(fā)送郵件
Java代碼實(shí)現(xiàn)
/** * Mail實(shí)現(xiàn)類 * 實(shí)現(xiàn) Cloneable 接口 * 重寫 Object.clone() 方法 */ public class Mail implements Cloneable { private String name; private String emailAddress; private String content; public Mail () { System.out.println("Mail Class Constructor!"); } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmailAddress() { return emailAddress; } public void setEmailAddress(String emailAddress) { this.emailAddress = emailAddress; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } @Override public String toString() { return "Mail{" + "name="" + name + """ + ", emailAddress="" + emailAddress + """ + ", content="" + content + """ + "}" + super.toString(); } @Override protected Object clone() throws CloneNotSupportedException { System.out.println("clone mail object!"); return super.clone(); } } /** * Mail工具類 */ public class MailUtil { public static void sendMail (Mail mail) { String outputContent = "向{0}同學(xué), 郵件地址:{1},郵件內(nèi)容:{2}, 發(fā)送郵件成功!"; System.out.println(MessageFormat.format( outputContent, mail.getName(), mail.getEmailAddress(), mail.getContent()) ); } public static void saveOriginMailRecord (Mail mail) { System.out.println("存儲(chǔ)originMail記錄, originMail: " + mail.getContent()); } }
測試與應(yīng)用
/** * 測試與應(yīng)用 */ public class Test { public static void main(String[] args) throws CloneNotSupportedException { Mail mail = new Mail(); mail.setContent("初始化模板"); System.out.println("初始化mail: " + mail.toString()); for (int i = 0; i < 10; i++) { Mail mailTemp = (Mail) mail.clone(); //并沒有調(diào)用Mail構(gòu)造器 mailTemp.setName("K.O_" + i); mailTemp.setEmailAddress("ko.shen_" + i + "@hotmail.com"); mailTemp.setContent("恭喜您中獎(jiǎng)了。"); MailUtil.sendMail(mailTemp); System.out.println("克隆的mailTemp: " + mailTemp.toString()); } MailUtil.saveOriginMailRecord(mail); } }
測試結(jié)果
Mail Class Constructor! 初始化mail: Mail{name="null", emailAddress="null", content="初始化模板"}org.ko.prototype.v2.Mail@1540e19d clone mail object! 向K.O_0同學(xué), 郵件地址:[email protected],郵件內(nèi)容:恭喜您中獎(jiǎng)了。, 發(fā)送郵件成功! 克隆的mailTemp: Mail{name="K.O_0", emailAddress="[email protected]", content="恭喜您中獎(jiǎng)了。"}org.ko.prototype.v2.Mail@677327b6 clone mail object! 向K.O_1同學(xué), 郵件地址:[email protected],郵件內(nèi)容:恭喜您中獎(jiǎng)了。, 發(fā)送郵件成功! 克隆的mailTemp: Mail{name="K.O_1", emailAddress="[email protected]", content="恭喜您中獎(jiǎng)了。"}org.ko.prototype.v2.Mail@14ae5a5 clone mail object! 向K.O_2同學(xué), 郵件地址:[email protected],郵件內(nèi)容:恭喜您中獎(jiǎng)了。, 發(fā)送郵件成功! 克隆的mailTemp: Mail{name="K.O_2", emailAddress="[email protected]", content="恭喜您中獎(jiǎng)了。"}org.ko.prototype.v2.Mail@7f31245a clone mail object! 向K.O_3同學(xué), 郵件地址:[email protected],郵件內(nèi)容:恭喜您中獎(jiǎng)了。, 發(fā)送郵件成功! 克隆的mailTemp: Mail{name="K.O_3", emailAddress="[email protected]", content="恭喜您中獎(jiǎng)了。"}org.ko.prototype.v2.Mail@6d6f6e28 clone mail object! 向K.O_4同學(xué), 郵件地址:[email protected],郵件內(nèi)容:恭喜您中獎(jiǎng)了。, 發(fā)送郵件成功! 克隆的mailTemp: Mail{name="K.O_4", emailAddress="[email protected]", content="恭喜您中獎(jiǎng)了。"}org.ko.prototype.v2.Mail@135fbaa4 clone mail object! 向K.O_5同學(xué), 郵件地址:[email protected],郵件內(nèi)容:恭喜您中獎(jiǎng)了。, 發(fā)送郵件成功! 克隆的mailTemp: Mail{name="K.O_5", emailAddress="[email protected]", content="恭喜您中獎(jiǎng)了。"}org.ko.prototype.v2.Mail@45ee12a7 clone mail object! 向K.O_6同學(xué), 郵件地址:[email protected],郵件內(nèi)容:恭喜您中獎(jiǎng)了。, 發(fā)送郵件成功! 克隆的mailTemp: Mail{name="K.O_6", emailAddress="[email protected]", content="恭喜您中獎(jiǎng)了。"}org.ko.prototype.v2.Mail@330bedb4 clone mail object! 向K.O_7同學(xué), 郵件地址:[email protected],郵件內(nèi)容:恭喜您中獎(jiǎng)了。, 發(fā)送郵件成功! 克隆的mailTemp: Mail{name="K.O_7", emailAddress="[email protected]", content="恭喜您中獎(jiǎng)了。"}org.ko.prototype.v2.Mail@2503dbd3 clone mail object! 向K.O_8同學(xué), 郵件地址:[email protected],郵件內(nèi)容:恭喜您中獎(jiǎng)了。, 發(fā)送郵件成功! 克隆的mailTemp: Mail{name="K.O_8", emailAddress="[email protected]", content="恭喜您中獎(jiǎng)了。"}org.ko.prototype.v2.Mail@4b67cf4d clone mail object! 向K.O_9同學(xué), 郵件地址:[email protected],郵件內(nèi)容:恭喜您中獎(jiǎng)了。, 發(fā)送郵件成功! 克隆的mailTemp: Mail{name="K.O_9", emailAddress="[email protected]", content="恭喜您中獎(jiǎng)了。"}org.ko.prototype.v2.Mail@7ea987ac 存儲(chǔ)originMail記錄, originMail: 初始化模板
從輸出信息可以看出來,使用clone方法不需要通過構(gòu)造函數(shù)創(chuàng)建
由于原型模式uml比較簡單,和上面基本一致,這里就不再介紹了!
0x06.擴(kuò)展(深拷貝與淺拷貝) 1. 淺克隆直接實(shí)現(xiàn)原型模式
/** * 淺克隆 */ public class Pig1 implements Cloneable { private String name; private Date birthday; public Pig1(String name, Date birthday) { this.name = name; this.birthday = birthday; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "Pig1{" + "name="" + name + """ + ", birthday=" + birthday + "}" + super.toString(); } }
測試類
public class Test1 { public static void main(String[] args) throws CloneNotSupportedException { //淺克隆, 沒辦法克隆引用對(duì)象 Date birthday = new Date(0l); Pig1 pig1 = new Pig1("佩奇", birthday); Pig1 pig2 = (Pig1) pig1.clone(); System.out.println(pig1); System.out.println(pig2); System.out.println("-------"); pig1.getBirthday().setTime(666666666666L); System.out.println(pig1); System.out.println(pig2); } }
輸出日志
Pig1{name="佩奇", birthday=Thu Jan 01 08:00:00 CST 1970}org.ko.prototype.clone.Pig1@6d6f6e28 Pig1{name="佩奇", birthday=Thu Jan 01 08:00:00 CST 1970}org.ko.prototype.clone.Pig1@135fbaa4 ------- Pig1{name="佩奇", birthday=Sat Feb 16 09:11:06 CST 1991}org.ko.prototype.clone.Pig1@6d6f6e28 Pig1{name="佩奇", birthday=Sat Feb 16 09:11:06 CST 1991}org.ko.prototype.clone.Pig1@135fbaa4
如上所示,修改pig1中的birthday,pig2中的也響應(yīng)了變化,所以直接使用clone方法返回的對(duì)象中的引用變量并沒有重新創(chuàng)建而是直接復(fù)用的原有對(duì)象中的變量。
由此得出結(jié)論:clone方法默認(rèn)使用的是淺拷貝。
如果想要引用變量也全部復(fù)制?
2.深拷貝其實(shí)深克隆只是自己實(shí)現(xiàn)了引用變量的創(chuàng)建,請(qǐng)看實(shí)現(xiàn):
/** * 深克隆 */ public class Pig2 implements Cloneable { private String name; private Date birthday; public Pig2(String name, Date birthday) { this.name = name; this.birthday = birthday; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } @Override protected Object clone() throws CloneNotSupportedException { Pig2 pig2 = (Pig2) super.clone(); //深克隆, 要自己去做對(duì)象引用的克隆 pig2.birthday = (Date) pig2.birthday.clone(); return pig2; } @Override public String toString() { return "Pig1{" + "name="" + name + """ + ", birthday=" + birthday + "}" + super.toString(); } }
測試類
public class Test2 { public static void main(String[] args) throws CloneNotSupportedException { //深克隆 Date birthday = new Date(0l); Pig2 p1 = new Pig2("佩奇", birthday); Pig2 p2 = (Pig2) p1.clone(); System.out.println(p1); System.out.println(p2); System.out.println("-------"); p1.getBirthday().setTime(666666666666L); System.out.println(p1); System.out.println(p2); } }
輸出日志
Pig2{name="佩奇", birthday=Thu Jan 01 08:00:00 CST 1970}org.ko.prototype.clone.Pig2@6d6f6e28 Pig2{name="佩奇", birthday=Thu Jan 01 08:00:00 CST 1970}org.ko.prototype.clone.Pig2@135fbaa4 ------- Pig2{name="佩奇", birthday=Sat Feb 16 09:11:06 CST 1991}org.ko.prototype.clone.Pig2@6d6f6e28 Pig2{name="佩奇", birthday=Thu Jan 01 08:00:00 CST 1970}org.ko.prototype.clone.Pig2@135fbaa4
可以看出,修改了pig1的時(shí)間,pig2并沒有跟著響應(yīng)。所以深拷貝完成。
0x07.原型模式對(duì)單例模式的破壞當(dāng)單例對(duì)象實(shí)現(xiàn)了clone方法時(shí),會(huì)返回多個(gè)實(shí)例,請(qǐng)看實(shí)現(xiàn):
/** * 簡單的餓漢式單例 */ public class StaticInnerClassSingleton implements Cloneable { /** * 看靜態(tài)類的初始化鎖那個(gè)線程可以拿到 */ private static class InnerClass { private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton(); } public static StaticInnerClassSingleton getInstance() { return InnerClass.staticInnerClassSingleton; } private StaticInnerClassSingleton() { if (InnerClass.staticInnerClassSingleton != null) { throw new RuntimeException("單例對(duì)象禁止反射調(diào)用"); } } /** * 直接重寫clone方法 * @return * @throws CloneNotSupportedException */ @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } public class Test { public static void main(String[] args) throws CloneNotSupportedException { //獲取單例對(duì)象 StaticInnerClassSingleton singleton = StaticInnerClassSingleton.getInstance(); System.out.println(singleton.toString()); //clone獲取克隆對(duì)象 StaticInnerClassSingleton singleton1 = (StaticInnerClassSingleton) singleton.clone(); System.out.println(singleton1.toString()); } }
輸出日志
org.ko.prototype.singleton.StaticInnerClassSingleton@1540e19d org.ko.prototype.singleton.StaticInnerClassSingleton@677327b6
根據(jù)日志可以看出,單例模式被破壞掉。
重寫clone()方法,直接返回INSTANCE對(duì)象解決原型模式對(duì)單例模式的破壞
/** * 簡單的餓漢式單例 */ public class StaticInnerClassSingleton1 implements Cloneable { /** * 看靜態(tài)類的初始化鎖那個(gè)線程可以拿到 */ private static class InnerClass { private static StaticInnerClassSingleton1 staticInnerClassSingleton = new StaticInnerClassSingleton1(); } public static StaticInnerClassSingleton1 getInstance() { return InnerClass.staticInnerClassSingleton; } private StaticInnerClassSingleton1() { if (InnerClass.staticInnerClassSingleton != null) { throw new RuntimeException("單例對(duì)象禁止反射調(diào)用"); } } /** * 修改克隆方法,返回單例對(duì)象 * @return * @throws CloneNotSupportedException */ @Override protected Object clone() throws CloneNotSupportedException { return InnerClass.staticInnerClassSingleton; } } /** * 修改后的測試類 */ public class Test1 { public static void main(String[] args) throws CloneNotSupportedException { //獲取單例對(duì)象 StaticInnerClassSingleton1 singleton1 = StaticInnerClassSingleton1.getInstance(); System.out.println(singleton1.toString()); //獲取clone對(duì)象 StaticInnerClassSingleton1 singleton2 = (StaticInnerClassSingleton1) singleton1.clone(); System.out.println(singleton2.toString()); } }
輸出日志
org.ko.prototype.singleton.StaticInnerClassSingleton1@1540e19d org.ko.prototype.singleton.StaticInnerClassSingleton1@1540e19d
可以看出,返回的對(duì)象地址時(shí)一致的。這樣就解決了原型對(duì)單例模式的破壞。
0x08.源碼地址原型模式: https://github.com/sigmako/design-pattern/tree/master/prototype
0x09.參考慕課網(wǎng)設(shè)計(jì)模式精講: https://coding.imooc.com/class/270.html
設(shè)計(jì)模式之原型模式: https://blog.csdn.net/chenliguan/article/details/69855738
C06 原型模式 示例(六) 原型模式破壞單例模式: https://blog.csdn.net/weixin_33669968/article/details/88889565
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/77862.html
摘要:如果一個(gè)對(duì)象的初始化需要很多其他對(duì)象的數(shù)據(jù)準(zhǔn)備或其他資源的繁瑣計(jì)算,那么可以使用原型模式。當(dāng)需要一個(gè)對(duì)象的大量公共信息,少量字段進(jìn)行個(gè)性化設(shè)置的時(shí)候,也可以使用原型模式拷貝出現(xiàn)有對(duì)象的副本進(jìn)行加工處理。 1、什么是原型模式Specify the kinds of objects to create using a prot...
摘要:三種使用構(gòu)造函數(shù)創(chuàng)建對(duì)象的方法和的作用都是在某個(gè)特殊對(duì)象的作用域中調(diào)用函數(shù)。這種方式還支持向構(gòu)造函數(shù)傳遞參數(shù)。叫法上把函數(shù)叫做構(gòu)造函數(shù),其他無區(qū)別適用情境可以在特殊的情況下用來為對(duì)象創(chuàng)建構(gòu)造函數(shù)。 一、工廠模式 工廠模式:使用字面量和object構(gòu)造函數(shù)會(huì)有很多重復(fù)代碼,在此基礎(chǔ)上改進(jìn)showImg(https://segmentfault.com/img/bVbmKxb?w=456&...
摘要:繼續(xù)分享設(shè)計(jì)模式的公開課,這是第四篇?jiǎng)?chuàng)建型模式之原型模式設(shè)計(jì)模式的一般介紹在第一篇文章講了,不了解的可以先看看。設(shè)計(jì)模式的第一部分,創(chuàng)建型模式就總結(jié)完了。下面還有兩部分結(jié)構(gòu)型設(shè)計(jì)模式和行為型設(shè)計(jì)模式稍后繼續(xù)。 繼續(xù)分享設(shè)計(jì)模式的公開課,這是第四篇?jiǎng)?chuàng)建型模式之原型模式 設(shè)計(jì)模式的一般介紹在第一篇文章講了,不了解的可以先看看。 原型模式: 用原型實(shí)例指定創(chuàng)建對(duì)象的種類,并且通過拷貝這個(gè)...
摘要:可以用刪除實(shí)例對(duì)象中自己添加的屬性可以確定屬性是原型中還是實(shí)例對(duì)象中,當(dāng)時(shí)實(shí)例對(duì)象中時(shí),返回的是操作符,有兩種使用方式,單獨(dú)使用和循環(huán)中。單獨(dú)使用,通過對(duì)象能夠訪問屬性時(shí)返回,無論時(shí)在原型中還是實(shí)例對(duì)象中。 原型模式,每個(gè)創(chuàng)建的對(duì)象都有一個(gè)prototype屬性,這個(gè)屬性是一個(gè)指針,指向一個(gè)對(duì)象,而這個(gè)對(duì)象的用途是包含可以由特定類型的所有實(shí)例共享的屬性和方法。 ------------...
摘要:深入系列第十四篇,講解創(chuàng)建對(duì)象的各種方式,以及優(yōu)缺點(diǎn)。也就是說打著構(gòu)造函數(shù)的幌子掛羊頭賣狗肉,你看創(chuàng)建的實(shí)例使用都無法指向構(gòu)造函數(shù)這樣方法可以在特殊情況下使用。 JavaScript深入系列第十四篇,講解創(chuàng)建對(duì)象的各種方式,以及優(yōu)缺點(diǎn)。 寫在前面 這篇文章講解創(chuàng)建對(duì)象的各種方式,以及優(yōu)缺點(diǎn)。 但是注意: 這篇文章更像是筆記,因?yàn)椤禞avaScript高級(jí)程序設(shè)計(jì)》寫得真是太好了! 1....
閱讀 1391·2021-11-22 09:34
閱讀 2592·2021-11-12 10:36
閱讀 1127·2021-11-11 16:55
閱讀 2343·2020-06-22 14:43
閱讀 1478·2019-08-30 15:55
閱讀 1992·2019-08-30 15:53
閱讀 1776·2019-08-30 10:50
閱讀 1234·2019-08-29 12:15