一、模式定義
通過(guò)復(fù)制現(xiàn)有實(shí)例來(lái)創(chuàng)建新的實(shí)例,無(wú)需知道相應(yīng)類(lèi)的信息。
二、模式使用場(chǎng)景
- 類(lèi)初始化需要
消耗非常多的資源。 - 通過(guò)new產(chǎn)生一個(gè)對(duì)象需要非常
繁瑣的數(shù)據(jù)準(zhǔn)備或訪問(wèn)權(quán)限。 - 提供給其他對(duì)象訪問(wèn),而且各個(gè)調(diào)用者可能需要
修改其值時(shí)。
三、代碼實(shí)現(xiàn)
public class WordDocument implements Cloneable {
private String mText;
private ArrayList<String> mImages = new ArrayList<>();
public WordDocument() {
}
@Override
public WordDocument clone() {
try {
WordDocument doc = (WordDocument) super.clone();
// 淺拷貝,只是單純的只想了內(nèi)存引用
doc.mText = this.mText;
doc.mImages = this.mImages;
// 深拷貝,操作副本時(shí)不會(huì)影響原始對(duì)象
doc.mImages = (ArrayList<String>) this.mImages.clone();
return doc;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//省略構(gòu)造函數(shù)...
}
四、深拷貝和淺拷貝的區(qū)別
-
深拷貝:拷貝時(shí)創(chuàng)建一個(gè)
新的對(duì)象,并且所有屬性都是新對(duì)象(指向新的內(nèi)存地址)。對(duì)復(fù)制后的對(duì)象進(jìn)行修改,不會(huì)影響原來(lái)對(duì)象的值。 -
淺拷貝:拷貝時(shí)創(chuàng)建一個(gè)新的對(duì)象,對(duì)象中的屬性如果是
基本數(shù)據(jù)類(lèi)型會(huì)新創(chuàng)建的對(duì)象,但是屬性是引用類(lèi)型不會(huì)創(chuàng)建新的對(duì)象,而是使用與之前的對(duì)象指向同一個(gè)內(nèi)存地址。對(duì)復(fù)制后的對(duì)象進(jìn)行修改,仍然會(huì)影響原來(lái)對(duì)象的值。
五、Android源碼分析
Android中的Intent就實(shí)現(xiàn)了Cloneable接口,但是clone()方法中卻是通過(guò)new來(lái)創(chuàng)建對(duì)象的。
public class Intent implements Parcelable, Cloneable {
//其他代碼略
@Override
public Object clone() {
//這里沒(méi)有調(diào)用super.clone()來(lái)實(shí)現(xiàn)拷貝,而是直接通過(guò)new來(lái)創(chuàng)建
return new Intent(this);
}
public Intent(Intent o) {
this.mAction = o.mAction;
this.mData = o.mData;
this.mType = o.mType;
this.mPackage = o.mPackage;
this.mComponent = o.mComponent;
this.mFlags = o.mFlags;
this.mContentUserHint = o.mContentUserHint;
if (o.mCategories != null) {
this.mCategories = new ArraySet<String>(o.mCategories);
}
if (o.mExtras != null) {
this.mExtras = new Bundle(o.mExtras);
}
if (o.mSourceBounds != null) {
this.mSourceBounds = new Rect(o.mSourceBounds);
}
if (o.mSelector != null) {
this.mSelector = new Intent(o.mSelector);
}
if (o.mClipData != null) {
this.mClipData = new ClipData(o.mClipData);
}
}
}
在《 Android 源碼設(shè)計(jì)模式解析與實(shí)戰(zhàn)》中講到,在 Intent 的 clone 方法中并沒(méi)有調(diào)用 super.clone() 方法來(lái)實(shí)現(xiàn)對(duì)象拷貝,而是調(diào)用 new Intent() 的原因是:使用 clone 和 new 需要根據(jù)構(gòu)造對(duì)象的成本來(lái)決定,如果對(duì)象的
構(gòu)造成本或者構(gòu)造較為麻煩的時(shí)候使用 clone 函數(shù)效率較高,否則可以使用 new 的形式。
六、模式優(yōu)缺點(diǎn)
-
優(yōu)點(diǎn)
- 原型模式是在內(nèi)存中二進(jìn)制流的拷貝,要比直接new一個(gè)對(duì)象性能好,特別是產(chǎn)生大量對(duì)象時(shí)。
- 隱藏制造新實(shí)例的重要性。
- 在重復(fù)創(chuàng)建相似對(duì)象的時(shí)候可以用。
-
缺點(diǎn)
- 直接在內(nèi)存中拷貝,
構(gòu)造函數(shù)是不會(huì)執(zhí)行的,應(yīng)該注意這個(gè)潛在問(wèn)題。 - 每個(gè)類(lèi)都必須配備一個(gè)克隆方法。
- 深拷貝會(huì)較為復(fù)雜。
- 直接在內(nèi)存中拷貝,
七、總結(jié)
原型模式本質(zhì)上就是對(duì)象拷貝,使用原型模式可以解決構(gòu)造復(fù)雜對(duì)象的資源消耗問(wèn)題,能夠在某些場(chǎng)景下提升創(chuàng)建對(duì)象的效率。還有一個(gè)重要用途就是保護(hù)性拷貝,也就是某個(gè)對(duì)象對(duì)外可能是只讀的,為了防止外部對(duì)這個(gè)只讀對(duì)象進(jìn)行修改,通常可以通過(guò)返回一個(gè)對(duì)象拷貝的形式實(shí)現(xiàn)只讀的限制。另外需要區(qū)別淺拷貝和深拷貝,它們之間容易出現(xiàn)問(wèn)題,