裝修推薦平臺(tái)星沙網(wǎng)站優(yōu)化seo
原型模式
緣起
某天,小明的Leader找到小明:“小明啊,如果有個(gè)發(fā)簡(jiǎn)歷的需求,就是有個(gè)簡(jiǎn)歷的模板,然后打印很多份,要去一份一份展示出來,用編程怎么實(shí)現(xiàn)呢?”
小明一聽,腦袋里就有了思路,二十分鐘后給了一版代碼
// 簡(jiǎn)歷類
public class Resume {private String name;private String sex;private String age;private String timeArea;private String company;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 timeArea, String company) {this.timeArea = timeArea;this.company = company;}// 展示簡(jiǎn)歷public void display() {System.out.println(this.name + " " + this.sex + " " + this.age);System.out.println("工作經(jīng)歷 " + this.timeArea + " " + this.company);}
}
客戶端代碼
public static void main(String[] args) {Resume resume1 = new Resume("小明");resume1.setPersonalInfo("男", "22");resume1.setWorkExperience("2021-2023", "XX公司");Resume resume2 = new Resume("小明");resume2.setPersonalInfo("男", "22");resume2.setWorkExperience("2021-2023", "XX公司");Resume resume3 = new Resume("小明");resume1.setPersonalInfo("男", "22");resume1.setWorkExperience("2021-2023", "XX公司");resume1.display();resume2.display();resume3.display();}
Leader看后,說道:“挺好,這其實(shí)就是我當(dāng)年手寫簡(jiǎn)歷時(shí)代的代碼哈哈哈,三份簡(jiǎn)歷需要實(shí)例化三次。你覺得這樣會(huì)不會(huì)麻煩呢?如果二十份簡(jiǎn)歷,你就要實(shí)例化二十次是不是;而且如果你寫錯(cuò)了一個(gè)字,那你就要改20次,你可以這么寫”
public class Test {public static void main(String[] args) {Resume resume1 = new Resume("小明");resume1.setPersonalInfo("男", "22");resume1.setWorkExperience("2021-2023", "XX公司");Resume resume2 = resume1;Resume resume3 = resume1;resume1.display();resume2.display();resume3.display();}
}
“其實(shí)就是傳遞引用對(duì)象,而不是傳值,這樣做就如同是在resume2、resume3
紙上是空白的,而將resume1上的內(nèi)容粘貼到了resume2、resume3
上面,你還有沒有其他的方式能實(shí)現(xiàn)呢?比如emmm,Clone克隆”。
原型模式
忙活了好一會(huì)兒,小明找到了一個(gè)相關(guān)的設(shè)計(jì)模式–原型模式。
原型模式(Prototype),用原型實(shí)例指定創(chuàng)建對(duì)象的種類,并且通過復(fù)制這些原型創(chuàng)建的對(duì)象。
原型模式其實(shí)就是從一個(gè)對(duì)象再創(chuàng)建另外一個(gè)可制定的對(duì)象,而且不需要知道任何的創(chuàng)建細(xì)節(jié)。
看下基本原型模式的代碼。
- 原型類
// 原型類
public abstract class Prototype implements Cloneable{private String id;public Prototype(String id) {this.id = id;}public String getId() {return id;}@Overrideprotected Object clone() {Object object = null;try {object = super.clone();} catch (CloneNotSupportedException e) {System.out.println("克隆異常");}return object;}
}
- 具體原型類
public class ConcretePrototype extends Prototype {public ConcretePrototype(String id) {super(id);}
}
- 客戶端調(diào)用
ConcretePrototype p1 = new ConcretePrototype("123456");
System.out.println("原型ID:" + p1.getId());ConcretePrototype p2 = (ConcretePrototype) p1.clone();
System.out.println("克隆ID:" + p2.getId());
這樣子只需要實(shí)例化一個(gè)對(duì)象,其他的類實(shí)例化時(shí),只需要克隆這個(gè)對(duì)象即可。
對(duì)于Java而言,那個(gè)原型抽象類Prototype
是用不到的,因?yàn)榭寺?shí)在是太常用了,所以Java提供了Cloneable接口,其中有一個(gè)唯一的方法就是clone()
,我們只需要實(shí)現(xiàn)這個(gè)接口就可以完成原型模式了。
簡(jiǎn)歷原型模式實(shí)現(xiàn)
小明二十分鐘后,第二版代碼出爐了。
// 簡(jiǎn)歷類
public class Resume implements Cloneable{private String name;private String sex;private String age;private String timeArea;private String company;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 timeArea, String company) {this.timeArea = timeArea;this.company = company;}// 展示簡(jiǎn)歷public void display() {System.out.println(this.name + " " + this.age + " " + this.age);System.out.println("工作經(jīng)歷 " + this.timeArea + " " + this.company);}@Overrideprotected Resume clone() throws CloneNotSupportedException {return (Resume) super.clone();}
}
- 客戶端調(diào)用
public class Test {public static void main(String[] args) throws CloneNotSupportedException {Resume resume1 = new Resume("小明");resume1.setPersonalInfo("男", "22");resume1.setWorkExperience("2021-2023", "XX公司");Resume resume2 = resume1.clone();Resume resume3 = resume1.clone();resume1.display();resume2.display();resume3.display();}
}
// 結(jié)果如下
小明 男 22
工作經(jīng)歷 2021-2023 XX公司
小明 男 22
工作經(jīng)歷 2021-2023 XX公司
小明 男 22
工作經(jīng)歷 2021-2023 XX公司
Leader看后點(diǎn)了點(diǎn)頭,“一般在初始化的信息不發(fā)生變化的情況下,克隆就是最好的辦法。這既隱藏了對(duì)象的創(chuàng)建細(xì)節(jié),又對(duì)性能是大大的提高。不用重新初始化對(duì)象,而是動(dòng)態(tài)獲得對(duì)象運(yùn)行時(shí)的狀態(tài)?!?/p>
Leader接著又問道:“別高興太早了,你知道這種clone有什么弊端嗎,或者說是需要注意的點(diǎn)呢”
小明搖了搖頭,Leader接著說:“你知道深淺拷貝吧,如果字段是值類型的,則對(duì)該字段逐位復(fù)制;如果是引用類型的則只復(fù)制引用,不復(fù)制引用的對(duì)象;因此,原始對(duì)象及其副本中的引用都是同一個(gè)對(duì)象”。
“你先把工作經(jīng)歷
單獨(dú)抽離出來,然后用簡(jiǎn)歷類使用它們?!?/p>
小明不到十分鐘,改完了。
- 簡(jiǎn)歷類
// 簡(jiǎn)歷類
public class Resume implements Cloneable{private String name;private String sex;private String age;private WorkExperience work;public Resume(String name) {this.name = name;this.work = new WorkExperience(); // 實(shí)例化工作經(jīng)歷對(duì)象}// 設(shè)置個(gè)人信息public void setPersonalInfo(String sex, String age) {this.sex = sex;this.age = age;}// 設(shè)置工作經(jīng)歷public void setWorkExperience(String timeArea, String company) {this.work.setTimeArea(timeArea);this.work.setCompany(company);}// 展示簡(jiǎn)歷public void display() {System.out.println(this.name + " " + this.sex + " " + this.age);System.out.println("工作經(jīng)歷 " + this.work.getTimeArea() + " " + this.work.getCompany());}@Overrideprotected Resume clone() throws CloneNotSupportedException {return (Resume) super.clone();}
}
- 工作經(jīng)歷類
public class WorkExperience {private String timeArea;private String company;public String getTimeArea() {return timeArea;}public void setTimeArea(String timeArea) {this.timeArea = timeArea;}public String getCompany() {return company;}public void setCompany(String company) {this.company = company;}
}
- 客戶端
public class Test {public static void main(String[] args) throws CloneNotSupportedException {Resume resume1 = new Resume("小明");resume1.setPersonalInfo("男", "22");resume1.setWorkExperience("2021-2023", "XX公司");Resume resume2 = resume1.clone();resume2.setWorkExperience("2001-2003", "ABC集團(tuán)");Resume resume3 = resume1.clone();resume2.setWorkExperience("2005-2007", "ABC公司");resume1.display();resume2.display();resume3.display();}
}
// 執(zhí)行結(jié)果如下
小明 男 22
工作經(jīng)歷 2005-2007 ABC公司
小明 男 22
工作經(jīng)歷 2005-2007 ABC公司
小明 男 22
工作經(jīng)歷 2005-2007 ABC公司
“看明白了吧,一個(gè)原型,兩個(gè)副本它們的,workExperience對(duì)象全都是同一個(gè)引用,所以你改一個(gè),其他的全都變了,這就是淺復(fù)制了。而我需要它們的workExperience對(duì)象全都是復(fù)制的對(duì)象,不能相同?!?/p>
簡(jiǎn)歷深拷貝實(shí)現(xiàn)
“實(shí)現(xiàn)這個(gè)其實(shí)很簡(jiǎn)單,就是你的被引用對(duì)象,也去實(shí)現(xiàn)Cloneable接口,實(shí)現(xiàn)clone()方法,然后在引用類中將它們處理下就行了,快去查下相關(guān)資料,實(shí)現(xiàn)一下試試”。
小明半小時(shí)后,新的代碼又出爐了。
- WorkExperience工作經(jīng)歷類
public class WorkExperience implements Cloneable{private String timeArea;private String company;@Overrideprotected WorkExperience clone() throws CloneNotSupportedException {return (WorkExperience) super.clone();}.....
}
- 簡(jiǎn)歷類
// 簡(jiǎn)歷類
public class Resume implements Cloneable{private String name;private String sex;private String age;private WorkExperience work;....@Overrideprotected Resume clone() throws CloneNotSupportedException {// 處理引用的對(duì)象Resume r = (Resume) super.clone();r.work = this.work.clone();return r;}
}
再來測(cè)試下。
小明 男 22
工作經(jīng)歷 2021-2023 XX公司
小明 男 22
工作經(jīng)歷 2005-2007 ABC公司
小明 男 22
工作經(jīng)歷 2021-2023 XX公司
總結(jié)
淺復(fù)制:被復(fù)制對(duì)象的所有變量都含有與原來的對(duì)象相同的值,而所有的對(duì)其他對(duì)象的引用都仍然指向原來的對(duì)象。
深復(fù)制:把引用對(duì)象的變量指向復(fù)制過的新對(duì)象,而不是原有的被引用的對(duì)象。