?????作者:貓十二懿
??賬號(hào):CSDN 、個(gè)人博客 、Github
??公眾號(hào):貓十二懿
原型模式
1、原型模式介紹
原型模式是一種創(chuàng)建型設(shè)計(jì)模式,它允許通過復(fù)制現(xiàn)有對(duì)象來生成新對(duì)象,而無需編寫從頭開始創(chuàng)建新對(duì)象的代碼。
1.1 具體介紹
在原型模式中,我們首先創(chuàng)建一個(gè)原型對(duì)象,然后通過復(fù)制該對(duì)象來創(chuàng)建新的實(shí)例,新的對(duì)象實(shí)例不需要知道任何創(chuàng)建的細(xì)節(jié),只需要知道如何復(fù)制即可得到一個(gè)與原型一模一樣的新對(duì)象。這種方法比直接創(chuàng)建對(duì)象要快,因?yàn)樵趶?fù)制過程中不需要執(zhí)行復(fù)雜的初始化操作。原型模式還可以減少代碼重復(fù),因?yàn)槲覀兛梢酝ㄟ^復(fù)制現(xiàn)有的對(duì)象來避免多次編寫相同的創(chuàng)建代碼。
在實(shí)現(xiàn)原型模式時(shí),我們通常使用一個(gè)原型管理器來存儲(chǔ)原型對(duì)象。這個(gè)管理器允許我們在需要時(shí)獲取原型對(duì)象的副本,而不是直接創(chuàng)建新對(duì)象。
原型模式在許多場景中都非常有用,例如在需要?jiǎng)?chuàng)建大量相似對(duì)象的情況下。它還可以用于避免復(fù)雜的初始化操作或構(gòu)造函數(shù),并且可以使代碼更加靈活和可擴(kuò)展。
1.2 原型模式角色
原型模式通常包括兩個(gè)角色:原型類和具體原型類。
- 原型類是一個(gè)抽象的類或接口,聲明了用于復(fù)制自己的方法。
- 具體原型類是具體的實(shí)現(xiàn)類,在實(shí)現(xiàn)父類(或接口)中定義的復(fù)制方法時(shí),需要注意實(shí)現(xiàn)深拷貝和淺拷貝,以確保復(fù)制出來的對(duì)象完全符合預(yù)期。
2、具體例子
2.1 違反原型模式例子
復(fù)印簡歷的例子,對(duì)于我們程序員來說,簡歷也是一個(gè)很重要的東西。
Resume
類
/**
* @author Shier
* CreateTime 2023/4/21 22:03
* 簡歷類
*/
public class Resume {
private String name;
private String sex;
private String age;
private String company;
private String workTime;
public Resume(String name) {
this.name = name;
}
/**
* 設(shè)置個(gè)人信息
*/
public void setPersonalInfo(String sex, String age) {
this.sex = sex;
this.age = age;
}
/**
* 設(shè)置工作經(jīng)歷
*/
public void setWorkExperience(String company, String workTime) {
this.company = company;
this.workTime = workTime;
}
/**
* 展示簡歷
*/
public void showResume() {
System.out.println("姓名:" + this.name + "\t年齡" + this.age + "\t性別" + this.sex);
System.out.println("工作經(jīng)歷:" + this.company + "\t時(shí)間:" + this.workTime);
}
}
測試類:
/**
* @author Shier
* CreateTime 2023/4/21 22:08
*/
public class ResumeTest1 {
public static void main(String[] args) {
Resume resume1 = new Resume("Shier");
resume1.setPersonalInfo("男", "19");
resume1.setWorkExperience("魚皮科技", "2023-04~05");
Resume resume2 = new Resume("Shier");
resume2.setPersonalInfo("男", "19");
resume2.setWorkExperience("魚皮科技", "2023-04~05");
Resume resume3 = new Resume("Shier");
resume3.setPersonalInfo("男", "19");
resume3.setWorkExperience("魚皮科技", "2023-04~05");
resume1.showResume();
resume2.showResume();
resume3.showResume();
}
}
最終結(jié)果顯示:
這樣就可以得到三分簡歷,但是你有沒有想過,如果我要準(zhǔn)備一百分呢?是不是就要去new 一百個(gè)Resume類,來創(chuàng)建新的對(duì)象。這樣做雖然是可以,但是重復(fù)的代碼也太多了吧,做重復(fù)的工作,而且消耗的內(nèi)存也多。
2.2 使用原型模式改進(jìn)
那個(gè)原型抽象類Prototype是用不著的,因?yàn)榭寺?shí)在是太常用了,所以Java提供了Cloneable接口,其中就是唯一的一個(gè)方法clone(),這樣你就只需要實(shí)現(xiàn)這個(gè)接口就可以完成原型模式了
改進(jìn)后的Resume類的UML類圖如下
具體代碼如下:
/**
* @author Shier
* CreateTime 2023/4/21 22:03
* 簡歷類
*/
public class Resume implements Cloneable {
private String name;
private String sex;
private String age;
private String company;
private String workTime;
public Resume(String name) {
this.name = name;
}
/**
* 設(shè)置個(gè)人信息
*/
public void setPersonalInfo(String sex, String age) {
this.sex = sex;
this.age = age;
}
/**
* 設(shè)置工作經(jīng)歷
*/
public void setWorkExperience(String company, String workTime) {
this.company = company;
this.workTime = workTime;
}
/**
* 展示簡歷
*/
public void showResume() {
System.out.println("姓名:" + this.name + "\t年齡" + this.age + "\t性別" + this.sex);
System.out.println("工作經(jīng)歷:" + this.company + "\t時(shí)間:" + this.workTime);
}
/**
* 實(shí)現(xiàn)clone方法
*/
public Resume clone() {
Resume object = null;
// 使用克隆對(duì)象進(jìn)行克隆內(nèi)容
try {
object = (Resume) super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("克隆異常了");
throw new RuntimeException(e);
}
return object;
}
}
測試類:
/**
* @author Shier
* CreateTime 2023/4/21 22:08
*/
public class ResumeTest1 {
public static void main(String[] args) {
Resume resume1 = new Resume("Shier");
resume1.setPersonalInfo("男", "19");
resume1.setWorkExperience("魚皮科技1", "2023-04~05");
// 使用resume1進(jìn)行調(diào)用clone對(duì)象
Resume resume2 = resume1.clone();
resume2.setWorkExperience("魚皮科技2", "2023-04~05");
Resume resume3 = resume1.clone();
resume3.setWorkExperience("魚皮科技3", "2023-04~05");
resume1.showResume();
resume2.showResume();
resume3.showResume();
}
}
結(jié)果同上
3 淺拷貝與深拷貝
現(xiàn)在’簡歷’對(duì)象里的數(shù)據(jù)都是String型的,而String是一種擁有值類型特點(diǎn)的特殊引用類型,super.clone()方法是這樣,如果字段是值類型的,則對(duì)該字段執(zhí)行逐位復(fù)制,如果字段是引用類型,則復(fù)制引用但不復(fù)制引用的對(duì)象;因此,原始對(duì)象及其副本引用同一對(duì)象。什么意思呢?就是說如果你的’簡歷’類當(dāng)中有對(duì)象引用,那么引用的對(duì)象數(shù)據(jù)是不會(huì)被克隆過來的。
淺拷貝:被復(fù)制對(duì)象的所有變量都含有與原來的對(duì)象相同的值,而所有的對(duì)其他對(duì)象的引用都仍然指向原來的對(duì)象。但我們可能更需要這樣的一種需求,把要復(fù)制的對(duì)象所引用的對(duì)象都復(fù)制一遍
3.1 淺拷貝例子
/**
* @author Shier
* CreateTime 2023/4/21 22:32
* 工作經(jīng)歷類
*/
public class WorkExperience {
private String company;
private String workTime;
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
public String getWorkTime() {
return workTime;
}
public void setWorkTime(String workTime) {
this.workTime = workTime;
}
}
/**
* @author Shier
* CreateTime 2023/4/21 22:03
* 簡歷類
*/
public class Resume implements Cloneable {
private String name;
private String sex;
private String age;
private WorkExperience workExperience;
public Resume(String name) {
this.name = name;
this.workExperience = new WorkExperience();
}
/**
* 設(shè)置個(gè)人信息
*/
public void setPersonalInfo(String sex, String age) {
this.sex = sex;
this.age = age;
}
/**
* 設(shè)置工作經(jīng)歷
*/
public void setWorkExperience(String company, String workTime) {
this.workExperience.setCompany(company);
this.workExperience.setWorkTime(workTime);
}
/**
* 展示簡歷
*/
public void showResume() {
System.out.println("姓名:" + this.name + "\t年齡" + this.age + "\t性別" + this.sex);
System.out.println("工作經(jīng)歷:" + this.workExperience.getCompany() + "\t時(shí)間:" + this.workExperience.getWorkTime());
}
/**
* 實(shí)現(xiàn)clone方法
*/
public Resume clone() {
Resume object = null;
// 使用克隆對(duì)象進(jìn)行克隆內(nèi)容
try {
object = (Resume) super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("克隆異常了");
throw new RuntimeException(e);
}
return object;
}
}
測試類:
/**
* @author Shier
* CreateTime 2023/4/21 22:08
*/
public class ResumeTest1 {
public static void main(String[] args) {
Resume resume1 = new Resume("Shier");
resume1.setPersonalInfo("男", "19");
resume1.setWorkExperience("魚皮科技1", "2023-04~05");
Resume resume2 = resume1.clone();
resume2.setWorkExperience("魚皮科技2", "2023-04~05");
Resume resume3 = resume1.clone();
resume3.setWorkExperience("魚皮科技3", "2023-04~05");
resume1.showResume();
resume2.showResume();
resume3.showResume();
}
}
結(jié)果顯示:
這個(gè)結(jié)果和我們的預(yù)期的不一樣,第三個(gè)把前面兩個(gè)都覆蓋掉了。
由于它是淺表拷貝,所以對(duì)于值類型,沒什么問題,對(duì)引用類型,就只是復(fù)制了引用,對(duì)引用的對(duì)象還是指向了原來的對(duì)象,所以就會(huì)出現(xiàn)我給resume1、resume2、resume3三個(gè)引用設(shè)置’工作經(jīng)歷’,但卻同時(shí)看到三個(gè)引用都是最后一次設(shè)置,因?yàn)槿齻€(gè)引用都指向了同一個(gè)對(duì)象。
3.2 深拷貝例子
3.2.1 深拷貝介紹
深拷貝把引用對(duì)象的變量指向復(fù)制過的新對(duì)象,而不是原有的被引用的對(duì)象。
深拷貝是指創(chuàng)建一個(gè)新對(duì)象,并將原始對(duì)象中的所有非靜態(tài)字段及其關(guān)聯(lián)對(duì)象的值復(fù)制到新對(duì)象中。如果字段是基本數(shù)據(jù)類型,則拷貝它們的值;如果字段是引用類型,則遞歸地拷貝它們所指向的對(duì)象,直到所有引用對(duì)象都被拷貝為止。因此,原始對(duì)象和副本對(duì)象將不共享任何對(duì)象。
3.2.2 改進(jìn)程序
再次修改上面的程序,UML類圖如下:
修改工作經(jīng)歷類:
package com.shier.sourcepattern;
/**
* @author Shier
* CreateTime 2023/4/21 22:32
* 工作經(jīng)歷類
*/
public class WorkExperience implements Cloneable{
private String company;
private String workTime;
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
public String getWorkTime() {
return workTime;
}
public void setWorkTime(String workTime) {
this.workTime = workTime;
}
/**
* 實(shí)現(xiàn)clone方法
*/
public WorkExperience clone() {
WorkExperience object = null;
// 使用克隆對(duì)象進(jìn)行克隆內(nèi)容
try {
object = (WorkExperience) super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("克隆異常了");
throw new RuntimeException(e);
}
return object;
}
}
修改簡歷類:
/**
* 實(shí)現(xiàn)clone方法
*/
public Resume clone() {
Resume object = null;
// 使用克隆對(duì)象進(jìn)行克隆內(nèi)容
try {
object = (Resume) super.clone();
// 進(jìn)行深拷貝
this.workExperience = this.workExperience.clone();
} catch (CloneNotSupportedException e) {
System.out.println("克隆異常了");
throw new RuntimeException(e);
}
return object;
}
測試的代碼不用改變
運(yùn)行結(jié)果顯示:
看到這個(gè)三個(gè)都是不同的,即達(dá)到了目的。
4、總結(jié)
原型模式通過使用原型管理器來存儲(chǔ)原型對(duì)象,并在需要時(shí)獲取原型對(duì)象的副本,以避免多次創(chuàng)建相同的對(duì)象。
原型模式優(yōu)點(diǎn):
- 可以在不編寫創(chuàng)建代碼的情況下創(chuàng)建新對(duì)象。
- 可以減少代碼重復(fù),因?yàn)槲覀兛梢酝ㄟ^拷貝現(xiàn)有對(duì)象來避免多次編寫相同的創(chuàng)建代碼。
- 可以減少初始化操作或構(gòu)造函數(shù),并使代碼更加靈活和可擴(kuò)展。
原型模式缺點(diǎn):
- 如果拷貝操作很復(fù)雜,可能會(huì)導(dǎo)致性能問題。
- 如果對(duì)象有循環(huán)依賴關(guān)系,則需要特殊處理。
原型模式應(yīng)用場景:
- 創(chuàng)建成本較大的對(duì)象:某些對(duì)象的創(chuàng)建過程需要耗費(fèi)大量時(shí)間和資源,例如數(shù)據(jù)庫連接對(duì)象、網(wǎng)絡(luò)連接對(duì)象等。在這種情況下,使用原型模式可以避免重復(fù)創(chuàng)建相同的對(duì)象,從而提高系統(tǒng)的性能和效率。
- 大量相似對(duì)象的創(chuàng)建:某些對(duì)象可能存在大量相似的情況,例如在圖形界面中創(chuàng)建圖形對(duì)象時(shí),往往會(huì)存在大量相似的圖形對(duì)象,只是具體屬性不同。在這種情況下,使用原型模式可以通過復(fù)制現(xiàn)有對(duì)象來創(chuàng)建新對(duì)象,避免從頭開始創(chuàng)建新對(duì)象的代碼。
- 對(duì)象的復(fù)雜組合:某些對(duì)象的創(chuàng)建需要組合多個(gè)對(duì)象,例如在設(shè)計(jì)圖形界面中的窗口對(duì)象時(shí),需要組合多個(gè)控件對(duì)象。在這種情況下,使用原型模式可以通過復(fù)制現(xiàn)有對(duì)象來創(chuàng)建新對(duì)象,并在需要的時(shí)候修改其中某些組件,從而簡化對(duì)象的創(chuàng)建過程。
4.2 深淺拷貝
淺拷貝的優(yōu)點(diǎn):
- 相對(duì)于深拷貝,淺拷貝的效率更高,因?yàn)樗皇强截愐?,而不是遞歸地拷貝所有相關(guān)對(duì)象。
- 淺拷貝對(duì)于某些對(duì)象來說是合適的,例如拷貝一些不包含引用類型字段的簡單對(duì)象。
淺拷貝的缺點(diǎn):
- 如果原始對(duì)象中的引用類型字段被修改了,那么拷貝后的對(duì)象也會(huì)被修改,這可能會(huì)導(dǎo)致意外的副作用。
深拷貝的優(yōu)點(diǎn):文章來源:http://www.zghlxwxcb.cn/news/detail-463444.html
- 深拷貝可以創(chuàng)建完全獨(dú)立的對(duì)象,與原始對(duì)象不共享任何引用對(duì)象。
- 深拷貝可以避免意外的副作用。
深拷貝的缺點(diǎn):文章來源地址http://www.zghlxwxcb.cn/news/detail-463444.html
- 相對(duì)于淺拷貝,深拷貝的效率更低,因?yàn)樗枰?mark>遞歸地拷貝所有相關(guān)對(duì)象。
- 深拷貝可能會(huì)導(dǎo)致循環(huán)引用問題,需要特殊處理。
建新對(duì)象的代碼。
3. 對(duì)象的復(fù)雜組合:某些對(duì)象的創(chuàng)建需要組合多個(gè)對(duì)象,例如在設(shè)計(jì)圖形界面中的窗口對(duì)象時(shí),需要組合多個(gè)控件對(duì)象。在這種情況下,使用原型模式可以通過復(fù)制現(xiàn)有對(duì)象來創(chuàng)建新對(duì)象,并在需要的時(shí)候修改其中某些組件,從而簡化對(duì)象的創(chuàng)建過程。
4.2 深淺拷貝
淺拷貝的優(yōu)點(diǎn):
- 相對(duì)于深拷貝,淺拷貝的效率更高,因?yàn)樗皇强截愐茫皇沁f歸地拷貝所有相關(guān)對(duì)象。
- 淺拷貝對(duì)于某些對(duì)象來說是合適的,例如拷貝一些不包含引用類型字段的簡單對(duì)象。
淺拷貝的缺點(diǎn):
- 如果原始對(duì)象中的引用類型字段被修改了,那么拷貝后的對(duì)象也會(huì)被修改,這可能會(huì)導(dǎo)致意外的副作用。
深拷貝的優(yōu)點(diǎn):
- 深拷貝可以創(chuàng)建完全獨(dú)立的對(duì)象,與原始對(duì)象不共享任何引用對(duì)象。
- 深拷貝可以避免意外的副作用。
深拷貝的缺點(diǎn):
- 相對(duì)于淺拷貝,深拷貝的效率更低,因?yàn)樗枰?mark>遞歸地拷貝所有相關(guān)對(duì)象。
- 深拷貝可能會(huì)導(dǎo)致循環(huán)引用問題,需要特殊處理。
到了這里,關(guān)于創(chuàng)建型設(shè)計(jì)模式03-原型模式的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!