橘子学设计模式之原型模式
一、什么是原型模式
關(guān)于什么是原型模式,我們看一個例子先。
我們都知道華夏民族的子孫都是女媧拿泥捏出來的,我們站在偉大人文始祖女媧的角度來思考一下這個過程。
剛開始女媧大佬精力旺盛,上來就是手捏你人泥人,精雕細(xì)琢出一個個帥哥美女。但是大佬也會累,你每次都從新開始捏一個泥人,真的很累。于是大佬想了一個新的方法,創(chuàng)造了一個模子,每次先從模子里面復(fù)刻出來一個泥人,也許此時泥人還不是最后的結(jié)果,但是大佬此時就能在這個復(fù)刻出來的模子上面做一個簡單的處理就能創(chuàng)建出一個新的泥人,想想這種操作也是極其的節(jié)省時間的。
其實這就是一個原型模式的體現(xiàn)。
# 我們這里來給原型模式下一個比較書面話的定義: 原型模式的工作原理很簡單:將一個原型對象傳給那個要發(fā)動創(chuàng)建的對象,這個要發(fā)動創(chuàng)建的對象通過請求原型對象拷貝自己來實現(xiàn)創(chuàng)建過程。由于在軟件系統(tǒng)中我們經(jīng)常會遇到需要創(chuàng)建多個相同或者相似對象的情況,因此原型模式在真實開發(fā)中的使用頻率還是非常高的。 原型模式是一種“另類”的創(chuàng)建型模式,創(chuàng)建克隆對象的工廠就是原型類自身(自身提供出一個創(chuàng)建的方法,調(diào)用創(chuàng)建出對應(yīng)的克隆對象),工廠方法由克隆方法來實現(xiàn)。 需要注意的是通過克隆方法所創(chuàng)建的對象是全新的對象,它們在內(nèi)存中擁有新的地址,通常 對克隆所產(chǎn)生的對象進(jìn)行修改對原型對象不會造成任何影響,每一個克隆對象都是相互獨立的。通過不同的方式修改可以得到一系列相似但不完全相同的對象。所有我們看到原型模式其實和女媧造人的例子極其的相似。而且通過例子和定義,我們基本能提煉出這個原型模式的一些角色組成。
- Prototype(抽象原型類):它是聲明克隆方法的接口,是所有具體原型類的公共父類,可以 是抽象類也可以是接口,甚至還可以是具體實現(xiàn)類。
- ConcretePrototype(具體原型類):它實現(xiàn)在抽象原型類中聲明的克隆方法,在克隆方法中 返回自己的一個克隆對象。
- Client(客戶類):讓一個原型對象克隆自身從而創(chuàng)建一個新的對象,在客戶類中只需要直 接實例化或通過工廠方法等方式創(chuàng)建一個原型對象,再通過調(diào)用該對象的克隆方法即可得到 多個相同的對象。由于客戶類針對抽象原型類Prototype編程,因此用戶可以根據(jù)需要選擇具體 原型類,系統(tǒng)具有較好的可擴(kuò)展性,增加或更換具體原型類都很方便。
所以我們看到原型模式的核心在于如何實現(xiàn)克隆方法,因為你此模式本身就是干這個的,所以這是他的核心工作。一般實現(xiàn)有兩種方式組成。
二、原型模式的實現(xiàn)方式
1、通用創(chuàng)建
所謂通用創(chuàng)建其實就是不依賴jdk提供的api,說白了就是自己實現(xiàn)了,不調(diào)用方法。
通用的克隆實現(xiàn)方法是在具體原型類的克隆方法中實例化一個與自身類型相同的對象并將其 返回,并將相關(guān)的參數(shù)傳入新創(chuàng)建的對象中,保證它們的成員屬性相同。示意代碼如下所 示:
頂級抽象類:
package com.liuwei.prototype.test1;/** * @Author author * @Description 接口定義 * @Date 19:05 2022-4-3 * @Param * @param null * @return **/ public interface Prototype { public String attr = ""; //成員屬性 public Prototype clone(); public void setAttr(String attr); public String getAttr(); }實現(xiàn)類:這是原型類,提供clone方法,其實就是自己new出來
package com.liuwei.prototype.test1;/** * @author: author * @description: 原型類,實現(xiàn)clone方法 * @date: 2022-4-3 19:06 * @version: 1.0 */ public class ConcretePrototype implements Prototype{ private String attr; //成員屬性 public void setAttr(String attr) { this.attr = attr; } public String getAttr() { return this.attr; } //克隆方法 public Prototype clone() { //創(chuàng)建新對象 Prototype prototype = new ConcretePrototype(); prototype.setAttr(this.attr); return prototype; } }客戶端:
package com.liuwei.prototype.test1;/** * @author: author * @description: 客戶端 * @date: 2022-4-3 19:10 * @version: 1.0 */ public class Client { public static void main(String[] args) { Prototype obj1 = new ConcretePrototype(); obj1.setAttr("SB"); Prototype obj2 = obj1.clone(); System.out.println(obj1); System.out.println(obj2); } }>>>>>>>>>>>輸出結(jié)果com.liuwei.prototype.test1.ConcretePrototype@29453f44com.liuwei.prototype.test1.ConcretePrototype@5cad8086這種方法可作為原型模式的通用實現(xiàn),它與編程語言特性無關(guān),任何面向?qū)ο笳Z言都可以使 用這種形式來實現(xiàn)對原型的克隆。 因為沒有使用的說固定的誰提供的API,所以其實不依賴于語言類型。
2、Java語言提供的clone()方法
學(xué)過Java語言的人都知道,所有的Java類都繼承自java.lang.Object。事實上,Object類提供一個 clone()方法,可以將一個Java對象復(fù)制一份。因此在Java中可以直接使用Object提供的clone()方 法來實現(xiàn)對象的克隆,Java語言中的原型模式實現(xiàn)很簡單。
需要注意的是能夠?qū)崿F(xiàn)克隆的Java類必須實現(xiàn)一個標(biāo)識接口Cloneable,表示這個Java類支持被復(fù)制。如果一個類沒有實現(xiàn)這個接口但是調(diào)用了clone()方法,Java編譯器將拋出一個 CloneNotSupportedException異常。如下代碼所示:
package com.liuwei.prototype.test2; import com.liuwei.prototype.Prototype;/** * @author: author * @description: TODO * @date: 2022-4-3 19:18 * @version: 1.0 */ class ConcretePrototype implements Cloneable { //自己實現(xiàn)Cloneable接口的clone方法 public Prototype clone() { Object object = null; try { // 直接調(diào)用Object類提供的克隆方法即可 object = super.clone(); } catch (CloneNotSupportedException exception) { System.err.println("Not support cloneable"); } return (Prototype ) object; }}在客戶端創(chuàng)建原型對象和克隆對象也很簡單,如下代碼所示:
Prototype obj1 = new ConcretePrototype();
Prototype obj2 = obj1.clone();
# 一般而言,Java語言中的clone()方法滿足: (1) 對任何對象x,都有x.clone() != x,即克隆對象與原型對象不是同一個對象; (2) 對任何對象x,都有x.clone().getClass() == x.getClass(),即克隆對象與原型對象的類型一 樣; (3) 如果對象x的equals()方法定義恰當(dāng),那么x.clone().equals(x)應(yīng)該成立。# 為了獲取對象的一份拷貝,我們可以直接利用Object類的clone()方法,具體步驟如下: (1) 在派生類中覆蓋基類的clone()方法,并聲明為public; (2) 在派生類的clone()方法中,調(diào)用super.clone(); (3)派生類需實現(xiàn)Cloneable接口。 此時,Object類相當(dāng)于抽象原型類,所有實現(xiàn)了Cloneable接口的類相當(dāng)于具體原型類。三、原型模式的使用
1、前情提要
SB軟件公司一直使用自行開發(fā)的一套OA (Office Automatic,辦公自動化)系統(tǒng)進(jìn)行日常工作 辦理,但在使用過程中,越來越多的人對工作周報的創(chuàng)建和編寫模塊產(chǎn)生了抱怨。追其原 因,SB軟件公司的OA管理員發(fā)現(xiàn),由于某些崗位每周工作存在重復(fù)性,工作周報內(nèi)容都 大同小異,如圖7-1工作周報示意圖。這些周報只有一些小地方存在差異,但是現(xiàn)行系統(tǒng)每周 默認(rèn)創(chuàng)建的周報都是空白報表,用戶只能通過重新輸入或不斷復(fù)制粘貼來填寫重復(fù)的周報內(nèi) 容,極大降低了工作效率,浪費寶貴的時間。如何快速創(chuàng)建相同或者相似的工作周報,成為 SB公司OA開發(fā)人員面臨的一個新問題。 SB公司的開發(fā)人員通過對問題進(jìn)行仔細(xì)分析,決定按照如下思路對工作周報模塊進(jìn)行重新 設(shè)計和實現(xiàn): (1)除了允許用戶創(chuàng)建新周報外,還允許用戶將創(chuàng)建好的周報保存為模板; (2)用戶在再次創(chuàng)建周報時,可以創(chuàng)建全新的周報,還可以選擇合適的模板復(fù)制生成一份相同 的周報,然后對新生成的周報根據(jù)實際情況進(jìn)行修改,產(chǎn)生新的周報。2、原型模式上手
類圖結(jié)構(gòu)如下:
WeeklyLog充當(dāng)具體原型類,Object類充當(dāng)抽象原型類,clone()方法為原型方法。 WeeklyLog類的代碼如下所示:
原型類:
package com.liuwei.prototype.test3;/*** @author: author* @description: TODO* @date: 2022-4-3 19:31* @version: 1.0*/ public class WeeklyLog implements Cloneable{private String name;private String date;private String content;//克隆方法clone(),此處使用Java語言提供的克隆機(jī)制public WeeklyLog clone() {Object obj = null;try {obj = super.clone();return (WeeklyLog)obj;}catch(CloneNotSupportedException e) {System.out.println("不支持復(fù)制!");return null;}}get set省略。 }客戶端:
package com.liuwei.prototype.test3; /** * @author: author * @description: TODO * @date: 2022-4-3 19:33 * @version: 1.0 */ public class Client { public static void main(String args[]) { WeeklyLog log_previous = new WeeklyLog(); //創(chuàng)建原型對象 log_previous.setName("劉德華"); log_previous.setDate("第12周"); log_previous.setContent("這周工作很忙,每天加班!"); System.out.println("****周報****"); System.out.println("周次:" + log_previous.getDate()); System.out.println("姓名:" + log_previous.getName()); System.out.println("內(nèi)容:" + log_previous.getContent()); System.out.println("--------------------------------"); WeeklyLog log_new; log_new = log_previous.clone(); //調(diào)用克隆方法創(chuàng)建克隆對象 log_new.setDate("第13周"); System.out.println("****周報****"); System.out.println("周次:" + log_new.getDate()); System.out.println("姓名:" + log_new.getName()); System.out.println("內(nèi)容:" + log_new.getContent()); } }通過已創(chuàng)建的工作周報可以快速創(chuàng)建新的周報,然后再根據(jù)需要修改周報,無須再從頭開始 創(chuàng)建。原型模式為工作流系統(tǒng)中任務(wù)單的快速生成提供了一種解決方案。
3、深拷貝和淺拷貝
通過引入原型模式,SB軟件公司OA系統(tǒng)支持工作周報的快速克隆,極大提高了工作周報 的編寫效率,受到員工的一致好評。但有員工又發(fā)現(xiàn)一個問題,有些工作周報帶有附件,例如經(jīng)理助理“小龍女”的周報通常附有本周項目進(jìn)展報告匯總表、本周客戶反饋信息匯總表等, 如果使用上述原型模式來復(fù)制周報,周報雖然可以復(fù)制,但是周報的附件并不能復(fù)制,這是由于什么原因?qū)е碌哪?#xff1f;如何才能實現(xiàn)周報和附件的同時復(fù)制呢? 我們在本節(jié)將討論如何解決這些問題。 在回答這些問題之前,先介紹一下兩種不同的克隆方法,淺克隆(ShallowClone)和深克隆 (DeepClone)。在Java語言中,數(shù)據(jù)類型分為值類型(基本數(shù)據(jù)類型)和引用類型,值類型包括 int、double、byte、boolean、char等簡單數(shù)據(jù)類型,引用類型包括類、接口、數(shù)組等復(fù)雜類型。淺克隆和深克隆的主要區(qū)別在于是否支持引用類型的成員變量的復(fù)制,下面將對兩者進(jìn)行詳細(xì)介紹。其意思就是我以前都是基本類型屬性,所以能拷貝。但是要是有對象這種屬性,那就復(fù)制不出來了,這就是涉及到深淺的拷貝。3.1、淺克隆
在淺克隆中,如果原型對象的成員變量是值類型,將復(fù)制一份給克隆對象;如果原型對象的 成員變量是引用類型,則將引用對象的地址復(fù)制一份給克隆對象,也就是說原型對象和克隆 對象的成員變量指向相同的內(nèi)存地址。簡單來說,在淺克隆中,當(dāng)對象被復(fù)制時只復(fù)制它本身和其中包含的值類型的成員變量,而引用類型的成員對象并沒有復(fù)制,淺克隆示意圖如圖下所示:
在Java語言中,通過覆蓋Object類的clone()方法可以實現(xiàn)淺克隆。為了讓大家更好地理解淺克 隆和深克隆的區(qū)別,我們首先使用淺克隆來實現(xiàn)工作周報和附件類的復(fù)制,其結(jié)構(gòu)如下圖:
帶附件的周報結(jié)構(gòu)圖(淺克隆):
附件類Attachment代碼如下:
package com.liuwei.prototype.test4;import lombok.Data;/*** @author: author* @description: 附件類* @date: 2022-4-3 20:52* @version: 1.0*/ @Data public class Attachment {private String name; //附件名public void download() {System.out.println("下載附件,文件名為" + name);} }工作周報WeeklyLog類:
package com.liuwei.prototype.test4;import lombok.Data;/*** @author: author* @description: 工作周報WeeklyLog* @date: 2022-4-3 20:53* @version: 1.0*/ @Data public class WeeklyLog implements Cloneable{//為了簡化設(shè)計和實現(xiàn),假設(shè)一份工作周報中只有一個附件對象,實際情況中可以包含多個附件,可以通過List等集合對象來實現(xiàn)private Attachment attachment;private String name;private String date;private String content;//使用clone()方法實現(xiàn)淺克隆public WeeklyLog clone() {Object obj = null;try {obj = super.clone();return (WeeklyLog)obj;}catch(CloneNotSupportedException e) {System.out.println("不支持復(fù)制!");return null;}} }客戶端:
package com.liuwei.prototype.test4;/*** @author: author* @description: 客戶端代碼如下所示:* @date: 2022-4-3 20:55* @version: 1.0*/ public class Client {public static void main(String[] args) {WeeklyLog log_previous, log_new;log_previous = new WeeklyLog(); //創(chuàng)建原型對象Attachment attachment = new Attachment(); //創(chuàng)建附件對象log_previous.setAttachment(attachment); //將附件添加到周報中log_new = log_previous.clone(); //調(diào)用克隆方法創(chuàng)建克隆對象// 比較周報System.out.println("周報是否相同? " + (log_previous == log_new));//比較附件System.out.println("附件是否相同? " + (log_previous.getAttachment() == attachment));} }>>>>>>>>>>>>運行結(jié)果 周報是否相同? false 附件是否相同? true 由于使用的是默認(rèn)的淺克隆技術(shù),因此工作周報對象復(fù)制成功,通過“==”比較原型對象和克隆對象 的內(nèi)存地址時輸出false;但是比較附件對象的內(nèi)存地址時輸出true,說明它們在內(nèi)存中是同一個對象。3.2、深克隆
在深克隆中,無論原型對象的成員變量是值類型還是引用類型,都將復(fù)制一份給克隆對象, 深克隆將原型對象的所有引用對象也復(fù)制一份給克隆對象。簡單來說,在深克隆中,除了對象本身被復(fù)制外,對象所包含的所有成員變量也將復(fù)制,深克隆示意如下圖所示:
在Java語言中,如果需要實現(xiàn)深克隆,可以通過序列化(Serialization)等方式來實現(xiàn)。序列化就 是將對象寫到流的過程,寫到流中的對象是原有對象的一個拷貝,而原對象仍然存在于內(nèi)存 中。通過序列化實現(xiàn)的拷貝不僅可以復(fù)制對象本身,而且可以復(fù)制其引用的成員對象,因此 通過序列化將對象寫到一個流中,再從流里將其讀出來,可以實現(xiàn)深克隆。需要注意的是能 夠?qū)崿F(xiàn)序列化的對象其類必須實現(xiàn)Serializable接口,否則無法實現(xiàn)序列化操作。下面我們使用 深克隆技術(shù)來實現(xiàn)工作周報和附件對象的復(fù)制,由于要將附件對象和工作周報對象都寫入流 中,因此兩個類均需要實現(xiàn)Serializable接口,帶附件的周報結(jié)構(gòu)圖(深克隆)如下圖所示:
附件類Attachment:
package com.liuwei.prototype.test5;import lombok.Data;import java.io.Serializable;/*** @author: author* @description: 附件類* @date: 2022-4-3 21:05* @version: 1.0*/ @Data public class Attachment implements Serializable {private String name; //附件名public void download() {System.out.println("下載附件,文件名為" + name);} }工作周報類WeeklyLog不再使用Java自帶的克隆機(jī)制,而是通過序列化來從頭實現(xiàn)對象的深克 隆,我們需要重新編寫clone()方法,修改后的代碼如下:
工作周報類:
package com.liuwei.prototype.test5;import lombok.Data;import java.io.*;/*** @author: author* @description: 工作周報類* @date: 2022-4-3 21:07* @version: 1.0*/ @Data public class WeeklyLog implements Serializable {private Attachment attachment;private String name;private String date;private String content;//使用序列化技術(shù)實現(xiàn)深克隆public WeeklyLog deepClone() throws IOException, ClassNotFoundException, OptionalDataException {//將對象寫入流中ByteArrayOutputStream bao=new ByteArrayOutputStream();ObjectOutputStream oos=new ObjectOutputStream(bao);oos.writeObject(this);//將對象從流中取出ByteArrayInputStream bis=new ByteArrayInputStream(bao.toByteArray());ObjectInputStream ois=new ObjectInputStream(bis);return (WeeklyLog)ois.readObject();} }客戶端:
package com.liuwei.prototype.test5;/*** @author: author* @description: 客戶端代碼* @date: 2022-4-3 21:10* @version: 1.0*/ public class Client {public static void main(String[] args) {WeeklyLog log_previous, log_new = null;log_previous = new WeeklyLog(); //創(chuàng)建原型對象Attachment attachment = new Attachment(); //創(chuàng)建附件對象log_previous.setAttachment(attachment); //將附件添加到周報try {log_new = log_previous.deepClone(); //調(diào)用深克隆}catch(Exception e) {System.err.println("克隆失敗!");}//比較周報System.out.println("周報是否相同? " + (log_previous == log_new));//比較附件System.out.println("附件是否相同? " + (log_previous.getAttachment() == log_new.getAttachment()));} } >>>>>>>>>>>>>運行結(jié)果 周報是否相同? false 附件是否相同? false 從輸出結(jié)果可以看出,由于使用了深克隆技術(shù),附件對象也得以復(fù)制,因此用“==”比較原型 對象的附件和克隆對象的附件時輸出結(jié)果均為false。深克隆技術(shù)實現(xiàn)了原型對象和克隆對象的完全獨立,對任意克隆對象的修改都不會給其他對象產(chǎn)生影響,是一種更為理想的克隆實現(xiàn)方式。Java語言提供的Cloneable接口和Serializable接口的代碼非常簡單,它們都是空接口,這種空接口也稱為標(biāo)識接口,標(biāo)識接口中沒有任何方法的定義,其作用是告訴JRE這些接口的實現(xiàn)類是否具有某個功能,如是否支持克隆、是否支持序列化等。
4、終極原型模式
4.1、原型管理器的引入和實現(xiàn)
這里其實就是一個工廠類對于原型的一些封裝管理,其余的其實不必過多理解。
原型管理器(Prototype Manager)是將多個原型對象存儲在一個集合中供客戶端使用,它是一個專門負(fù)責(zé)克隆對象的工廠,其中定義了一個集合用于存儲原型對象,如果需要某個原型對象的一個克隆,可以通過復(fù)制集合中對應(yīng)的原型對象來獲得。在原型管理器中針對抽象原型類進(jìn)行編程,以便擴(kuò)展。帶原型管理器的原型模式,其結(jié)構(gòu)如下圖所示:
下面通過模擬一個簡單的公文管理器來介紹原型管理器的設(shè)計與實現(xiàn): SB軟件公司在日常 辦公中有許多公文需要創(chuàng)建、遞交和審批,例如《可行性分析報告》、《立項建議書》、 《軟件需求規(guī)格說明書》、《項目進(jìn)展報告》等,為了提高工作效率,在OA系統(tǒng)中為各類公文均創(chuàng)建了模板,用戶可以通過這些模板快速創(chuàng)建新的公文,這些公文模板需要統(tǒng)一進(jìn)行管理,系統(tǒng)根據(jù)用戶請求的不同生成不同的新公文。
我們使用帶原型管理器的原型模式實現(xiàn)公文管理器的設(shè)計, 公文管理器結(jié)構(gòu)圖如下圖所示:
以下是實現(xiàn)該功能的一些核心代碼,考慮到代碼的可讀性,我們對所有的類都進(jìn)行了簡化:
抽象公文接口:
package com.liuwei.prototype.test6;/*** @author: author* @description: 抽象公文接口,也可定義為抽象類,提供clone()方法的實現(xiàn),將業(yè)務(wù)方法聲明為抽象方法* @date: 2022-4-3 21:22* @version: 1.0*/ public interface OfficialDocument extends Cloneable{public OfficialDocument clone();public void display(); }可行性分析報告(Feasibility Analysis Report)類
package com.liuwei.prototype.test6;/*** @author: author* @description: 可行性分析報告(Feasibility Analysis Report)類,一種策略實現(xiàn)* @date: 2022-4-3 21:24* @version: 1.0*/ public class FAR implements OfficialDocument{@Overridepublic OfficialDocument clone() {OfficialDocument far = null;try {far = (OfficialDocument)super.clone();}catch(CloneNotSupportedException e) {System.out.println("不支持復(fù)制!");}return far;}@Overridepublic void display() {System.out.println("《可行性分析報告》");} }軟件需求規(guī)格說明書(Software Requirements Specification)類
package com.liuwei.prototype.test6;/*** @author: author* @description: 軟件需求規(guī)格說明書(Software Requirements Specification)類* @date: 2022-4-3 21:25* @version: 1.0*/ class SRS implements OfficialDocument {public OfficialDocument clone() {OfficialDocument srs = null;try {srs = (OfficialDocument)super.clone();}catch(CloneNotSupportedException e) {System.out.println("不支持復(fù)制!");}return srs;}public void display() {System.out.println("《軟件需求規(guī)格說明書》");} }原型管理器(使用餓漢式單例實現(xiàn)):
package com.liuwei.prototype.test6;import java.util.Hashtable;/*** @author: author* @description: 原型管理器(使用餓漢式單例實現(xiàn)),這里類似一個工廠類了* @date: 2022-4-3 21:26* @version: 1.0*/ public class PrototypeManager {//定義一個Hashtable,用于存儲原型對象private Hashtable ht=new Hashtable();//初始化實例對象,這里new的時候就把公文對象塞進(jìn)去private static PrototypeManager pm = new PrototypeManager();//為Hashtable增加公文對象,構(gòu)造初始化的時候添加對象類private PrototypeManager() {ht.put("far",new FAR());ht.put("srs",new SRS());}//增加新的公文對象public void addOfficialDocument(String key,OfficialDocument doc) {ht.put(key,doc);}//通過淺克隆獲取新的公文對象public OfficialDocument getOfficialDocument(String key) {return ((OfficialDocument)ht.get(key)).clone();}//get方法public static PrototypeManager getPrototypeManager(){return pm;} }客戶端:
package com.liuwei.prototype.test6;/*** @author: author* @description: 客戶端* @date: 2022-4-3 21:28* @version: 1.0*/ public class Client {public static void main(String[] args) {//獲取原型管理器對象PrototypeManager pm = PrototypeManager.getPrototypeManager();OfficialDocument doc1,doc2,doc3,doc4;doc1 = pm.getOfficialDocument("far");doc1.display();doc2 = pm.getOfficialDocument("far");doc2.display();System.out.println(doc1 == doc2);doc3 = pm.getOfficialDocument("srs");doc3.display();doc4 = pm.getOfficialDocument("srs");doc4.display();System.out.println(doc3 == doc4);} } >>>>>>>>>>運行結(jié)果 《可行性分析報告》 《可行性分析報告》 false 《軟件需求規(guī)格說明書》 《軟件需求規(guī)格說明書》 false # 原理:其實就是引入了個工廠 在PrototypeManager中定義了一個Hashtable類型的集合對象,使用“鍵值對”來存儲原型對象, 客戶端可以通過Key(如“far”或“srs”)來獲取對應(yīng)原型對象的克隆對象。PrototypeManager類 提供了類似工廠方法的getOfficialDocument()方法用于返回一個克隆對象。在本實例代碼中, 我們將PrototypeManager設(shè)計為單例類,使用餓漢式單例實現(xiàn),確保系統(tǒng)中有且僅有一個 PrototypeManager對象,有利于節(jié)省系統(tǒng)資源,并可以更好地對原型管理器對象進(jìn)行控制。四、原型模式總結(jié)
原型模式作為一種快速創(chuàng)建大量相同或相似對象的方式,在軟件開發(fā)中應(yīng)用較為廣泛,很多軟件提供的復(fù)制(Ctrl + C)和粘貼(Ctrl + V)操作就是原型模式的典型應(yīng)用,下面對該模式的使用效果和適用情況進(jìn)行簡單的總結(jié)。
# 1.主要優(yōu)點 原型模式的主要優(yōu)點如下: (1) 當(dāng)創(chuàng)建新的對象實例較為復(fù)雜時,使用原型模式可以簡化對象的創(chuàng)建過程,通過復(fù)制一個已有實例可以提高新實例的創(chuàng)建效率。 (2) 擴(kuò)展性較好,由于在原型模式中提供了抽象原型類,在客戶端可以針對抽象原型類進(jìn)行編 程,而將具體原型類寫在配置文件中,增加或減少產(chǎn)品類對原有系統(tǒng)都沒有任何影響。 (3) 原型模式提供了簡化的創(chuàng)建結(jié)構(gòu),工廠方法模式常常需要有一個與產(chǎn)品類等級結(jié)構(gòu)相同的工廠等級結(jié)構(gòu),而原型模式就不需要這樣,原型模式中產(chǎn)品的復(fù)制是通過封裝在原型類中的 克隆方法實現(xiàn)的,無須專門的工廠類來創(chuàng)建產(chǎn)品。 (4) 可以使用深克隆的方式保存對象的狀態(tài),使用原型模式將對象復(fù)制一份并將其狀態(tài)保存起來,以便在需要的時候使用(如恢復(fù)到某一歷史狀態(tài)),可輔助實現(xiàn)撤銷操作。 # 2.主要缺點 原型模式的主要缺點如下: (1) 需要為每一個類配備一個克隆方法,而且該克隆方法位于一個類的內(nèi)部,當(dāng)對已有的類進(jìn)行改造時,需要修改源代碼,違背了“開閉原則”。 (2) 在實現(xiàn)深克隆時需要編寫較為復(fù)雜的代碼,而且當(dāng)對象之間存在多重的嵌套引用時,為了實現(xiàn)深克隆,每一層對象對應(yīng)的類都必須支持深克隆,實現(xiàn)起來可能會比較麻煩。 # 3.適用場景 在以下情況下可以考慮使用原型模式: (1) 創(chuàng)建新對象成本較大(如初始化需要占用較長的時間,占用太多的CPU資源或網(wǎng)絡(luò)資源),新的對象可以通過原型模式對已有對象進(jìn)行復(fù)制來獲得,如果是相似對象,則可以對 其成員變量稍作修改。 (2) 如果系統(tǒng)要保存對象的狀態(tài),而對象的狀態(tài)變化很小,或者對象本身占用內(nèi)存較少時,可以使用原型模式配合備忘錄模式來實現(xiàn)。 (3) 需要避免使用分層次的工廠類來創(chuàng)建分層次的對象,并且類的實例對象只有一個或很少的幾個組合狀態(tài),通過復(fù)制原型對象得到新實例可能比使用構(gòu)造函數(shù)創(chuàng)建一個新實例更加方便。總結(jié)
以上是生活随笔為你收集整理的橘子学设计模式之原型模式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Ubuntu中解/压缩命令
- 下一篇: 十个必备的.NET开发小工具