源碼地址 | https://github.com/DingMouRen/DesignPattern |
---|
原型模式.png
- Prototype 抽象原型類 聲明一個克隆自身的接口
- ConcreteProtype 具體的原型類,實現一個克隆自身的操作
定義
原型模式用原型實例指定創建對象的種類,并通過拷貝這些原型創建新的對象
使用場景
- 類初始化需要消耗非常多的資源,包含數據、硬件資源等,通過原型拷貝避免這些消耗
- 通過new創建一個對象,需要繁雜的數據準備或者訪問權限等,這里可以使用原型模式
- 一個對象需要提供給其他對象訪問,各個調用者都有可能修改其值,可以使用原型模式拷貝多個對象供調用者使用,保護性拷貝。
協作
客戶端請求一個原型克隆自身。
舉個栗子
以文檔的拷貝為例,文檔中含有文本和圖片。為了安全,我們需要將原來的文檔拷貝一份副本,在副本上進行修改。
淺拷貝
淺拷貝是副本文檔的字段引用原始文檔字段的結果。Cloneable是標識性接口。注意:通過clone拷貝對象時,并不會執行構造函數。淺拷貝會有一個問題,就是下面添加圖片的時候,原型文檔也發生了變化。這是因為操作的images是同一個對象,通過深拷貝就可以避免這個問題。
//Cloneable代表Prototype,WordDocument代表ConcretePrototype
public class WordDocument implements Cloneable {
//文本
private String text;
//圖片列表
private ArrayList<String> images = new ArrayList<>();
@Override
protected Object clone() {
try {
WordDocument document = (WordDocument) super.clone();
document.text = this.text;
document.images = this.images;
return document;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
//打印文檔內容
public void showDocument(){
System.out.println("-------- 文檔內容: --------");
System.out.println("Text:"+text);
System.out.print("Images:");
for (String imgName : images){
System.out.print(imgName+",");
}
System.out.println();
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public ArrayList<String> getImages() {
return images;
}
public void addImage(String img) {
this.images.add(img);
}
}
public static void main(String[] args) {
//構建文檔對象
WordDocument originDoc = new WordDocument();
//編輯文檔,添加圖片
originDoc.setText("文本1");
originDoc.addImage("圖片1");
originDoc.addImage("圖片2");
originDoc.addImage("圖片3");
originDoc.addImage("圖片4");
//以原型文檔為原型,拷貝一份副本
WordDocument doc2 = (WordDocument) originDoc.clone();
doc2.showDocument();
//修改文檔副本,不會影響原始文檔
doc2.setText("我修改了文本呢");
doc2.addImage("新圖片");
doc2.showDocument();
//原型文檔
originDoc.showDocument();
}
深拷貝
//Cloneable代表Prototype,WordDocument代表ConcretePrototype
public class WordDocument implements Cloneable {
//文本
private String text;
//圖片列表
private ArrayList<String> images = new ArrayList<>();
@Override
protected Object clone() {
try {
WordDocument document = (WordDocument) super.clone();
document.text = this.text;
//對圖片images對象也調用clone()函數,進行深拷貝
document.images = (ArrayList<String>) this.images.clone();
return document;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
//打印文檔內容
public void showDocument(){
System.out.println("-------- 文檔內容: --------");
System.out.println("Text:"+text);
System.out.print("Images:");
for (String imgName : images){
System.out.print(imgName+",");
}
System.out.println();
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public ArrayList<String> getImages() {
return images;
}
public void addImage(String img) {
this.images.add(img);
}
}
public static void main(String[] args) {
//構建文檔對象
WordDocument originDoc = new WordDocument();
//編輯文檔,添加圖片
originDoc.setText("文本1");
originDoc.addImage("圖片1");
originDoc.addImage("圖片2");
originDoc.addImage("圖片3");
originDoc.addImage("圖片4");
//以原型文檔為原型,拷貝一份副本
WordDocument doc2 = (WordDocument) originDoc.clone();
doc2.showDocument();
//修改文檔副本,不會影響原始文檔
doc2.setText("我修改了文本呢");
doc2.addImage("新圖片");
doc2.showDocument();
//原型文檔
originDoc.showDocument();
}
總結
原型模式可以解決構建復雜對象的資源消耗問題,能夠在某些場景下提高創建對象的效率,同時也可以保護原型文件,也就是保護性拷貝,某個對象對外可能是只讀的,為了防止外部對這個只讀對象進行修改。
優點:
原型模式是在內存中二進制流的拷貝,比new性能要好,特別是在循環體內創建大量對象時。
缺點:
因為是在內存中拷貝,所以構造函數式不會執行的。所以實際應用時一定要注意這個問題。