原型設計模式

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源碼設計模式解析與實戰》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章