學(xué)習(xí)《Android 源碼設(shè)計模式解析與實踐》系列筆記
介紹
原型模式是一種創(chuàng)建型的模式。
原型模式就是用戶從一個樣板實例對象中復(fù)制出一個內(nèi)部屬性一致的對象,這個過程也可以稱作“克隆”。
原型模式多用于創(chuàng)建復(fù)雜的或者構(gòu)造耗時的實例。
定義
用原型實例指定創(chuàng)建對象的種類,并通過拷貝這些原型創(chuàng)建新的對象。
使用場景
- 類初始化需要消化非常多的資源,這個資源包括數(shù)據(jù)、硬件資源等,通過原型拷貝避免這些消耗。
- 通過 new 產(chǎn)生一個對象需要非常繁瑣的數(shù)據(jù)準(zhǔn)備貨訪問權(quán)限。
- 一個對象需要提供給其他對象訪問,而且各個調(diào)用者可能都需要修改其值時,可以考慮使用原型模式拷貝多個對象供調(diào)用者使用,即保護(hù)性拷貝。
結(jié)構(gòu)

- Client:使用者
- Prototype:抽象類或者接口,聲明具備 clone 能力
- ConcretePrototype:具體的原型類
實現(xiàn)
以文檔拷貝為例,文檔中有文字和圖片,用戶經(jīng)過了長時間編輯后,打算對文檔進(jìn)行一次較大改動,但是又怕修改的不滿意,所以將當(dāng)前的文本保存好,然后拷貝一份副本再修改。這里的原始文本就是樣本實例,也就是被“克隆”的對象,也就是原型。
具體代碼如下:
/**
* 文檔類型,也就是 UML 圖中的 ConcretePrototype 類,而 Cloneable 是 Prototype。
*/
public class WordDocument implements Cloneable {
private String mText; //文本
private ArrayList<String> mImages = new ArrayList<>(); // 圖片列表
public WordDocument() {
System.out.println("WordDocument()");
}
public String getText() {
return mText;
}
public void setText(String mText) {
this.mText = mText;
}
public ArrayList<String> getImages() {
return mImages;
}
public void setImages(ArrayList<String> mImages) {
this.mImages = mImages;
}
public void addImage(String image) {
if (mImages == null) {
mImages = new ArrayList<>();
}
mImages.add(image);
}
@Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
public void showDocument() {
System.out.println("----------------------------------");
System.out.println("WordDocument:" + this);
System.out.println("WordDocument mText:" + mText);
System.out.println("WordDocument mImages:" + mImages);
for (String image : mImages) {
System.out.println("WordDocument image name:" + image);
}
}
}
public class Client {
public static void main(String args []) {
WordDocument orgDoc = new WordDocument();
orgDoc.setText("document 1111");
orgDoc.addImage("image 111");
orgDoc.addImage("image 222");
orgDoc.showDocument();
WordDocument copyDoc = (WordDocument) orgDoc.clone();
copyDoc.showDocument();
copyDoc.setText("document 222");
copyDoc.addImage("image 333");
copyDoc.showDocument();
orgDoc.showDocument();
}
}
輸出結(jié)果:
WordDocument()
----------------------------------
WordDocument:test.linpu.com.myapplication.prototype.WordDocument@1b6d3586
WordDocument mText:document 1111
WordDocument mImages:[image 111, image 222]
WordDocument image name:image 111
WordDocument image name:image 222
----------------------------------
WordDocument:test.linpu.com.myapplication.prototype.WordDocument@4554617c
WordDocument mText:document 1111
WordDocument mImages:[image 111, image 222]
WordDocument image name:image 111
WordDocument image name:image 222
----------------------------------
WordDocument:test.linpu.com.myapplication.prototype.WordDocument@4554617c
WordDocument mText:document 222
WordDocument mImages:[image 111, image 222, image 333]
WordDocument image name:image 111
WordDocument image name:image 222
WordDocument image name:image 333
----------------------------------
WordDocument:test.linpu.com.myapplication.prototype.WordDocument@1b6d3586
WordDocument mText:document 1111
WordDocument mImages:[image 111, image 222, image 333]
WordDocument image name:image 111
WordDocument image name:image 222
WordDocument image name:image 333
上面的例子,通過 WordDocument copyDoc = (WordDocument) orgDoc.clone(); 進(jìn)行了副本的拷貝。
但是,從輸出的 log 中,我們發(fā)現(xiàn)了幾個問題:
-
WordDocument的構(gòu)造函數(shù)只調(diào)用了一次,也就是說orgDoc.clone()生成copyDoc對象是不會調(diào)用構(gòu)造函數(shù)的。 - 修改
copyDoc的mText成員屬性不會影響到orgDoc的mText成員屬性,但是修改mImages后就會影響。這是因為mText是基礎(chǔ)數(shù)據(jù)類型,拷貝的時候是值傳遞。mImages是引用類型,我們這里的拷貝是淺拷貝,淺拷貝對引用類型的成員變量,只是拷貝了引用地址,它們指向的都是同一地址空間,所以任何一個對象修改了mImages的值,都會對另外一個產(chǎn)生影響。
關(guān)于淺拷貝和深拷貝,可以參考文章:
Java 淺拷貝和深拷貝
淺拷貝會帶來數(shù)據(jù)安全方面的隱患,所以,我們進(jìn)一步修改代碼,達(dá)到深拷貝的目的。
這里主要是修改 WordDocument 的 clone() 方法,對引用類型的成員變量也進(jìn)行一次拷貝。
/**
* 文檔類型,也就是 UML 圖中的 ConcretePrototype 類,而 Cloneable 是 Prototype。
*/
public class WordDocument implements Cloneable {
private String mText; //文本
private ArrayList<String> mImages = new ArrayList<>(); // 圖片列表
public WordDocument() {
System.out.println("WordDocument()");
}
public String getText() {
return mText;
}
public void setText(String mText) {
this.mText = mText;
}
public ArrayList<String> getImages() {
return mImages;
}
public void setImages(ArrayList<String> mImages) {
this.mImages = mImages;
}
public void addImage(String image) {
if (mImages == null) {
mImages = new ArrayList<>();
}
mImages.add(image);
}
@Override
public Object clone() {
try {
WordDocument document = (WordDocument) super.clone();
document.mImages = (ArrayList<String>) mImages.clone();
return document;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
public void showDocument() {
System.out.println("----------------------------------");
System.out.println("WordDocument:" + this);
System.out.println("WordDocument mText:" + mText);
System.out.println("WordDocument mImages:" + mImages);
for (String image : mImages) {
System.out.println("WordDocument image name:" + image);
}
}
}
輸出結(jié)果:
WordDocument()
----------------------------------
WordDocument:test.linpu.com.myapplication.prototype.WordDocument@1b6d3586
WordDocument mText:document 1111
WordDocument mImages:[image 111, image 222]
WordDocument image name:image 111
WordDocument image name:image 222
----------------------------------
WordDocument:test.linpu.com.myapplication.prototype.WordDocument@4554617c
WordDocument mText:document 1111
WordDocument mImages:[image 111, image 222]
WordDocument image name:image 111
WordDocument image name:image 222
----------------------------------
WordDocument:test.linpu.com.myapplication.prototype.WordDocument@4554617c
WordDocument mText:document 222
WordDocument mImages:[image 111, image 222, image 333]
WordDocument image name:image 111
WordDocument image name:image 222
WordDocument image name:image 333
----------------------------------
WordDocument:test.linpu.com.myapplication.prototype.WordDocument@1b6d3586
WordDocument mText:document 1111
WordDocument mImages:[image 111, image 222]
WordDocument image name:image 111
WordDocument image name:image 222
以上的輸出結(jié)果可見,通過深拷貝和,修改 copyDoc 的 mImages 不會再影響到 orgDoc 的 mImages,因為此時的 mImages 是兩個不同的對象。
源碼中的原型模式
- ArrayList
如上述例子中可知,ArrayList 也是實現(xiàn)了 clone() 接口的。
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
- Intent
Android 中,使用的較為多的 Intent 也是使用了原型模式。
其簡單使用如下:
Uri uri = Uri.parse("tel:10086");
Intent shareIntent = new Intent(Intent.ACTION_CALL, uri);
// 克隆副本
Intent copyIntent = (Intent) shareIntent.clone();
其源碼如下:
@Override
public Object clone() {
return new Intent(this);
}
public Intent(Intent o) {
this(o, COPY_MODE_ALL);
}
private Intent(Intent o, @CopyMode int copyMode) {
this.mAction = o.mAction;
this.mData = o.mData;
this.mType = o.mType;
this.mPackage = o.mPackage;
this.mComponent = o.mComponent;
if (o.mCategories != null) {
this.mCategories = new ArraySet<>(o.mCategories);
}
if (copyMode != COPY_MODE_FILTER) {
this.mFlags = o.mFlags;
this.mContentUserHint = o.mContentUserHint;
this.mLaunchToken = o.mLaunchToken;
if (o.mSourceBounds != null) {
this.mSourceBounds = new Rect(o.mSourceBounds);
}
if (o.mSelector != null) {
this.mSelector = new Intent(o.mSelector);
}
if (copyMode != COPY_MODE_HISTORY) {
if (o.mExtras != null) {
this.mExtras = new Bundle(o.mExtras);
}
if (o.mClipData != null) {
this.mClipData = new ClipData(o.mClipData);
}
} else {
if (o.mExtras != null && !o.mExtras.maybeIsEmpty()) {
this.mExtras = Bundle.STRIPPED;
}
// Also set "stripped" clip data when we ever log mClipData in the (broadcast)
// history.
}
}
}
可見,Intent 的拷貝不是通過 super.clone() 來實現(xiàn)的,而是通過 new Intent(this),調(diào)用了其構(gòu)造函數(shù)。
總結(jié)
原型模式的本質(zhì)就是對象拷貝??截惙譃闇\拷貝和深拷貝。
原型模式主要有兩個用途:
- 解決構(gòu)建復(fù)雜對象的資源消耗問題,能夠在某些場景提升創(chuàng)建對象的效率。
- 對原型對象進(jìn)行保護(hù)。當(dāng)某個對象對外可能是只讀的,為了防止外部對這個只讀對象修改,可以通過返回一個對象拷貝的形式實現(xiàn)只讀的限制。
相關(guān)文章:
設(shè)計模式整理(1) 代理模式
設(shè)計模式整理(2) 單例模式
設(shè)計模式整理(3) Builder 模式
設(shè)計模式整理(4) 原型模式
設(shè)計模式整理(5) 工廠模式
設(shè)計模式整理(6) 策略模式
設(shè)計模式整理(7) 狀態(tài)模式
設(shè)計模式整理(8) 責(zé)任鏈模式
設(shè)計模式整理(9) 觀察者模式
設(shè)計模式整理(10) 適配器模式
設(shè)計模式整理(11) 裝飾模式
設(shè)計模式整理(12) 中介者模式