前言#
今天是周五了,在美好的周末到來之前趕緊寫點(diǎn)東西來證明自己這周沒有當(dāng)一條咸魚。
原型模式:用原型實(shí)例指定創(chuàng)建對(duì)象的種類,并且通過拷貝這些原型來創(chuàng)建新的對(duì)象。
這句話看著高大上,實(shí)際上是我從網(wǎng)上拷貝過來的,原型模式跟我們之前的寫過的幾種模式,例如觀察者,代理模式等等,是不一樣的,與其說他是一種模式,不如說是一種優(yōu)化,因?yàn)樗]有改變?cè)瓉淼拇a邏輯結(jié)構(gòu),僅僅是在創(chuàng)建對(duì)象上,進(jìn)行了優(yōu)化。
正文#
Object本身就有clone方法,這個(gè)方法是protect修飾符,所以我們需要把clone方法暴露出來,看一下Student的代碼示例:
public class Student implements Cloneable {
private String name = "xxx";
private String sex = "man";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
return new Student();
}
}
@Override
public String toString() {
return "my name is " + getName() + ":" + hashCode();
}
}
要想把clone方法暴露出來,需要一下兩個(gè)步驟:
1、實(shí)現(xiàn)Cloneable接口,雖然這個(gè)接口里并沒有方法。
2、創(chuàng)建一個(gè)public方法,調(diào)用clone方法,并實(shí)現(xiàn)自己的邏輯。
ok,運(yùn)行一下,看看有沒有效果:
Student student = new Student();
// 修改一下默認(rèn)的student名字,為了證明調(diào)用的是clone方法,而不是new
student.setName("lisi");
Student student1 = (Student) student.clone();
System.out.println(student.toString());
System.out.println(student1.toString());
從上面的運(yùn)行結(jié)果上看,通過clone方法創(chuàng)建并返回了一個(gè)新的Student對(duì)象。
那么new創(chuàng)建對(duì)象和clone創(chuàng)建對(duì)象的運(yùn)行效率如何呢?我們分別創(chuàng)建10000個(gè),對(duì)比一下耗時(shí)的時(shí)間:
從結(jié)果上看,創(chuàng)建10000個(gè)對(duì)象,clone的耗時(shí)要比new多耗費(fèi)了5毫秒,雖然速度稍慢,但是效率還是不錯(cuò)的。
(別著急,關(guān)于這個(gè)效率的問題,后面還有更具體的驗(yàn)證)
既然new關(guān)鍵字的效率比clone要高,我們還要clone有個(gè)卵用??
原型模式的使用場景
為什么要使用原型模式呢?首先我們要回到我們學(xué)習(xí)設(shè)計(jì)模式的起點(diǎn):
設(shè)計(jì)模式更多的是為了提高代碼可維護(hù)性,擴(kuò)展性,可讀性,對(duì)各個(gè)模塊之間進(jìn)行解耦,利于開發(fā)和維護(hù)。
有些時(shí)候我們不得不犧牲掉在可以接受的范圍內(nèi)的運(yùn)行效率,來完成目標(biāo)模式。
魚和熊掌不可得兼,所以我們要權(quán)衡犧牲的代價(jià)和得到的收益,過分追求效率,可能會(huì)讓我們迷失了真正的目標(biāo)。
說了這么多,原型模式適合什么樣的場景呢:
某一個(gè)類(Class),可以大量復(fù)用已有的對(duì)象的屬性,適合原型模式。
也就是說,我們需要的類的新對(duì)象和已有的對(duì)象的相比,幾乎不需要修改就可以直接用,而且屬性越多,原型模式體現(xiàn)的優(yōu)勢(shì)越好,光說不練沒有用,下面就舉個(gè)例子:
某市進(jìn)行高效運(yùn)動(dòng)會(huì),我們需要統(tǒng)計(jì)各個(gè)學(xué)校參加的學(xué)生,學(xué)生資料中,包含了學(xué)校的信息。
先定義學(xué)校School對(duì)象:
/**
* Created by li.zhipeng on 2017/9/15.
* <p>
* 自定義學(xué)生類
*/
public class Student implements Cloneable {
// 以下全是學(xué)校資料
private String area; // 所在的區(qū)
private String address; // 地址
private String slogan; // 學(xué)校的宣傳語
private int level; // 學(xué)校的級(jí)別
private int teacherNum; // 老師的數(shù)量
private int studentNum; // 學(xué)生的數(shù)量
... // 還有很多信息
// 以下全是學(xué)生資料
private String name = "xxx";
private String sex = "boy";
}
我們看到在Student中,如果是同一個(gè)學(xué)校的學(xué)生,我們只要修改其中的name和sex就可以了,我有兩種方法通過學(xué)生a,得到同一個(gè)學(xué)校的同學(xué)b的信息:
// 通過new
Student b = new Student();
b.setArea(a.getArea());
b.setAddress(a.getAddress());
b.setSlogan(a.getSlogan());
b.setLevel(a.getLevel());
b.setTeacherNum(a.getTeacherNum());
b.setStudentNum(a.getStudentNum());
b.setName("zhangsan");
b.setSex("boy");
// 通過clone
Student c = (Student) a.clone();
c.setName("wangwu");
c.setSex("boy");
是不是對(duì)比非常明顯,相信這個(gè)時(shí)候你已經(jīng)看到了原型模式的代碼是多么的簡潔。
為clone的效率討一個(gè)說法
我們剛才驗(yàn)證了,如果不重寫構(gòu)造方法,屬性很少的情況下,new確實(shí)比clone的效率要高,但是如果需要重寫構(gòu)造方法,或者是屬性很多呢?
1、通過new關(guān)鍵字,創(chuàng)建屬性很多的對(duì)象,取3組數(shù)據(jù):
long time = System.currentTimeMillis();
for (int i = 0; i < length; i++) {
Student student = new Student();
}
// 打印時(shí)間...
time = System.currentTimeMillis();
for (int i = 0; i < length; i++) {
Student student1 = (Student) original.clone();
}
// 打印時(shí)間...
經(jīng)過對(duì)比,如果是創(chuàng)建對(duì)象屬性很多的對(duì)象,new比clone確實(shí)要快一些。
2、對(duì)比new創(chuàng)建了之后,還要通過setter方法改變屬性,去3組數(shù)據(jù):
long time = System.currentTimeMillis();
for (int i = 0; i < length; i++) {
Student b = new Student();
b.setArea(original.getArea());
b.setAddress(original.getAddress());
b.setSlogan(original.getSlogan());
b.setLevel(original.getLevel());
b.setTeacherNum(original.getTeacherNum());
b.setStudentNum(original.getStudentNum());
b.setName("zhangsan");
b.setSex("boy");
}
// 打印時(shí)間...
time = System.currentTimeMillis();
for (int i = 0; i < length; i++) {
Student c = (Student) original.clone();
c.setName("wangwu");
c.setSex("boy");
}
// 打印時(shí)間...
由結(jié)果我們可以看出,setter方法是比較耗時(shí)的,通過原型模式,我們盡可能的避免setter,所以在運(yùn)行時(shí)間上已經(jīng)顯示出優(yōu)勢(shì)。
3、重寫Student默認(rèn)的構(gòu)造方法,取3組數(shù)據(jù):
public Student(){
System.out.println("Create Student Object...");
}
// 測試代碼與第2中情況相同
是不是覺得十分意外,從結(jié)果上我們發(fā)現(xiàn)了clone是會(huì)調(diào)用類的構(gòu)造方法的,僅僅是對(duì)對(duì)象的克隆,所以在運(yùn)行時(shí)間上的優(yōu)勢(shì)大幅提升。
總結(jié)#
回顧一下今天的內(nèi)容:
1、首先我們了解了原型模式的定義,以及clone方法的使用步驟。
2、了解了原型模式的使用場景,實(shí)際體會(huì)原型模式在代碼上為我們帶來的簡潔體驗(yàn)。
3、根據(jù)不同情況,對(duì)比new和clone創(chuàng)建對(duì)象的效率,clone的運(yùn)行效率在大部分應(yīng)用場景會(huì)讓創(chuàng)建對(duì)象的速度更快,效率更高。
ok,今天所有的內(nèi)容就結(jié)束了,祝大家周末愉快~