(《設計模式解析與實戰》讀書筆記)
一、定義
用原型實例指定創建對象的種類,並通過拷貝這些原型創建新的對象;
二、使用場景
(1)類初始化需要消化非常多的資源,這個資源包括數據、硬件資源等,通過原型拷貝避免這些消耗;
(2)通過new產生一個對象需要非常繁瑣的數據準備或訪問權限,這時可使用原型模式;
(3)一個對象需要提供給其他對象訪問,而且各個調用者可能都需要修改其值時,可以考慮使用原型模式拷貝多個對象供調用者使用,即保護性拷貝。
三、簡單示例
修改一份文檔,我們一般不會直接修改“原型”,而是拷貝一份副本進行修改。
/**
* 文檔是具體的原型類;Cloneable是接口,聲明具備clone能力
*/
public class WordDocument implements Cloneable {
// 文本
private String mText;
// 圖片列表
private ArrayList<String> mImages = new ArrayList<>();
public WordDocument() {
System.out.println("------我是WordDocument構造函數-------");
}
@Override
protected WordDocument clone() {
try {
WordDocument doc = (WordDocument) super.clone();
doc.mText = this.mText;
doc.mImages = this.mImages;
return doc;
} catch (Exception e) {
}
return null;
}
public String getmText() {
return mText;
}
public void setmText(String mText) {
this.mText = mText;
}
public ArrayList<String> getmImages() {
return mImages;
}
public void addImages(String img) {
this.mImages.add(img);
}
/**
* 打印文檔內容
*/
public void showDocument() {
System.out.println("-----開始打印-----");
System.out.println("Text :" + mText);
System.out.println("Images List : ");
for (String imgName : mImages) {
System.out.println("image name : " + imgName);
}
System.out.println("-----打印結束-----");
}
}
public class Client {
public static void main(String[] args) {
// 1、構建文檔對象
WordDocument document = new WordDocument();
// 2、修改文檔
document.setmText("這是一篇文檔");
document.addImages("圖片1");
document.addImages("圖片2");
document.showDocument();
// 以原始文檔爲原型,拷貝一份副本
WordDocument document2 = document.clone();
System.out.println("這是未修改的副本");
document2.showDocument();
// 修改文檔副本,不會影響原始文檔
document2.setmText("這是修改後的副本");
document2.showDocument();
// 驗證原始文檔是否被修改
document.showDocument();
}
}
輸出後的結果:
可以看出在副本上修改不會影響到原型。並且通過clone拷貝對象時並不會執行構造函數。
修改Client代碼:
// 修改文檔副本
document2.setmText("這是修改後的副本");
document2.addImages("副本圖片.jpg");
document2.showDocument();
運行結果:
會發現原型被修改了。原因是上述方法只是淺拷貝,document2的mImages只是單純的指向了this.mImages引用,並沒有重新構造一個mImages對象。
運用深拷貝可解決這個問題,在拷貝時,對於引用型的字段也要採用拷貝的形式,而不是引用的形式。
修改clone()方法:
@Override
protected WordDocument clone() {
try {
WordDocument doc = (WordDocument) super.clone();
doc.mText = this.mText;
// 對mImages對象調用clone()函數,進行深拷貝
doc.mImages = (ArrayList<String>) this.mImages.clone();
return doc;
} catch (Exception e) {
}
return null;
}
運行後發現副本添加圖片並不會影響到原型。
所以在使用該模式時儘量使用深拷貝,避免操作副本時影響原始對象的問題。
四、優缺點
優點:
運行模式是在內存中二進制流的拷貝,要比直接new一個對象性能好很多,特別是要在一個循環體內產生大量的對象時,原型模式可以更好的體現其優點。
缺點:
直接在內存中拷貝,構造函數是不會執行的,在實際開發中應該注意這個潛在的問題。優點是減少了約束,缺點也是減少了約束,需要我們在實際應用中考慮。