1.定義:
原型模式(Prototype Pattern)是用於創建重複的對象,同時又能保證性能。這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。
這種模式是實現了一個原型接口,該接口用於創建當前對象的克隆。當直接創建對象的代價比較大時,則採用這種模式。例如,一個對象需要在一個高代價的數據庫操作之後被創建。我們可以緩存該對象,在下一個請求時返回它的克隆,在需要的時候更新數據庫,以此來減少數據庫調用。(即數據持久化)
2.使用場景
- 應用實例:
1、資源優化場景。
2、類初始化需要消化非常多的資源,這個資源包括數據、硬件資源等。
3、性能和安全要求的場景。
4、通過 new 產生一個對象需要非常繁瑣的數據準備或訪問權限,則可以使用原型模式。
5、一個對象多個修改者的場景。
6、一個對象需要提供給其他對象訪問,而且各個調用者可能都需要修改其值時,可以考慮使用原型模式拷貝多個對象供調用者使用。
7、在實際項目中,原型模式很少單獨出現,一般是和工廠方法模式一起出現,通過 clone 的方法創建一個對象,然後由工廠方法提供給調用者。
3.UML圖示
4.簡單示例
以原型模式的核心是clone方法,通過該方法進行拷貝,這裏舉一個名片拷貝的例子。
現在已經流行電子名片了,只要掃一下就可以將名片拷貝到自己的名片庫中, 我們先實現名片類。
public class BusinessCard implements Cloneable {
private String name;
private String company;
public BusinessCard() {
System.out.println("執行了構造方法BusinessCard構造方法");
}
@Override
protected BusinessCard clone() {
BusinessCard card=null;
try {
card= (BusinessCard) super.clone();
return card;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
public String getName() {
return name;
}
public String getCompany() {
return company;
}
public void show(){
System.out.println("BusinessCard{" +
"name='" + name + '\'' +
", company='" + company + '\'' +
'}');
}
}
public static void main(String[] args) {
BusinessCard card=new BusinessCard();
card.setName("張三");
card.setCompany("阿里");
BusinessCard clone = card.clone();
clone.setName("李四");
clone.setCompany("百度");
BusinessCard clone2 = card.clone();
clone2.setName("王五");
clone2.setCompany("騰訊");
card.show();
clone.show();
clone2.show();
}
運行結果
5.淺拷貝和深拷貝
原型模式涉及到淺拷貝和深拷貝的知識點,爲了更好的理解它們,還需要舉一些例子。
5.1實現淺拷貝
上述的例子中,BusinessCard的字段都是String類型的,如果字段是引用的類型的,會出現什麼情況呢
package com.onexzgj.yuanxing;
public class DeepBusinessCard implements Cloneable {
private String name;
private Company company =new Company();
public DeepBusinessCard() {
System.out.println("執行了構造方法BusinessCard構造方法");
}
@Override
protected DeepBusinessCard clone() {
DeepBusinessCard card=null;
try {
card= (DeepBusinessCard) super.clone();
return card;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
....
public void setCompany(String name,String address) {
this.company.setName(name);
this.company.setAddress(address);
}
public void show(){
System.out.println("BusinessCard{" +
"name='" + name + '\'' +
", address='" + company.getAddress() + '\'' +"name"+company.getName()+
'}');
}
}
public class Client {
public static void main(String[] args) {
DeepBusinessCard card=new DeepBusinessCard();
card.setName("張三");
card.setCompany("阿里","望京");
DeepBusinessCard clone = card.clone();
clone.setName("李四");
clone.setCompany("百度","中關村");
DeepBusinessCard clone2 = card.clone();
clone2.setName("王五");
clone2.setCompany("騰訊","西二旗");
clone.show();
card.show();
clone2.show();
}
}
運行結果
從結果可以看出company字段爲最後設置的”騰訊”、”西二旗”。這是因爲Object類提供的clone方法,不會拷貝對象中的內部數組和引用對象,導致它們仍舊指向原來對象的內部元素地址,這種拷貝叫做淺拷貝。
5.2 實現深拷貝
首先修改Company類實現Cloneable接口
public class Company implements Cloneable{
private String name;
private String address;
...
public Company clone(){
Company company=null;
try {
company= (Company) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return company;
}
}
爲了實現Company類能被拷貝,Company類也需要實現Cloneable接口並且覆寫clone方法。接着修改DeepBusinessCard的clone方法:
public class DeepBusinessCard implements Cloneable {
private String name;
private Company company = new Company();
...
@Override
public DeepBusinessCard clone() {
DeepBusinessCard businessCard = null;
try {
businessCard = (DeepBusinessCard) super.clone();
businessCard.company = this.company.clone();//1
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return businessCard;
}
...
}
運行結果
6.優缺點比價
優點:
1、性能提高。
2、逃避構造函數的約束。缺點:
1、配備克隆方法需要對類的功能進行通盤考慮,這對於全新的類不是很難,但對於已有的類不一定很容易,特別當一個類引用不支持串行化的間接對象,或者引用含有循環結構的時候。
2、必須實現 Cloneable 接口。
3、直接在內存中拷貝,構造函數是不會執行的,這樣就減少了約束,這既是優點也是缺點,需要在實際應用中去考量。
參考文章:
劉望舒博客
《Android源碼設計模式解析與實戰》