<<設計模式之禪(第二版)>>——第十三章 原型模式

定義:
  • 用原型實例指定創建對象的種類,并且通過拷貝這些原型創建新的對象
通用類圖:
原型模式通用類圖
/*
 * 實現Cloneable接口,復習父類中的clone方法
 * */
public class Prototype implements Cloneable {
  @Override
  public Prototype clone(){
    // TODO Auto-generated method stub
    Prototype prototype = null;
    try {
        prototype = (Prototype) super.clone();
    } catch (Exception e) {
        // TODO Auto-generated catch block
        // 相關的異常處理
        e.printStackTrace();
    }
    return prototype;
  }

}
原型模式的優點(通常是和工廠方法模式一起出現,通過clone的方法創建一個對象,然后由工廠方法提供給調用者):
  • 性能優良,原型模式是對內存中二進制流的拷貝,要比直接new一個對象的性能好很多
  • 逃避構造函數的約束,直接在內存中拷貝,構造函數是不會執行的。
原型模式的使用場景:
  • 資源優化場景,類初始化需要消耗非常多的資源,數據、硬件資源等
  • 性能和安全要求的場景,通過new產生一個對象需要非常繁瑣的數據準備或訪問權限
  • 一個對象多個修改者
原型模式的注意事項
  • 構造函數不會被執行(new 會執行構造函數中的方法,通過clone則不會執行構造函數中的方法)
  • 淺拷貝和深拷貝
  /*
  * 淺拷貝代碼
  * */
  public class ThingSimple implements Cloneable {
    // 定義一個私有變量
    private ArrayList<String> arrayList = new ArrayList();

    @Override
    public ThingSimple clone() {
    // TODO Auto-generated method stub
    ThingSimple thingSimple = null;
    try {
        thingSimple = (ThingSimple) super.clone();
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    return thingSimple;
    }

    public void setValue(String value) {
    this.arrayList.add(value);
    }

    public ArrayList<String> getValue() {

    return this.arrayList;
    }
  }
  public class ThingDeep implements Cloneable {
    // 定義一個私有變量
    private ArrayList<String> arrayList = new ArrayList();

    @Override
    public ThingDeep clone() {
    // TODO Auto-generated method stub
    ThingDeep thingDeep = null;
    try {
        thingDeep = (ThingDeep) super.clone();
        thingDeep.arrayList = (ArrayList<String>) this.arrayList.clone();
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    return thingDeep;
    }

    public void setValue(String value) {
    this.arrayList.add(value);
    }

    public ArrayList<String> getValue() {

    return this.arrayList;
    }

  }

  public class Client {
    public static void main(String[] args) {
      // 測試淺拷貝
      ThingSimple thingSimple = new ThingSimple();
      thingSimple.setValue("1");

      ThingSimple copySimple = thingSimple.clone();
      copySimple.setValue("2");
      System.out.println("原數據-->" + thingSimple.getValue());
      System.out.println("拷貝數據-->" + copySimple.getValue());
      System.out.println(">>>>>>>>>>>>>>>");
      // 測試深拷貝
      ThingDeep thingDeep = new ThingDeep();
      thingDeep.setValue("3");
      ThingDeep copyDeep = thingDeep.clone();
      copyDeep.setValue("4");
      System.out.println("原數據-->" + thingDeep.getValue());
      System.out.println("拷貝數據-->" + copyDeep.getValue());
    }
  }


 原數據-->[1, 2]
 拷貝數據-->[1, 2]
 >>>>>>>>>>>>>>>
 原數據-->[3]
 拷貝數據-->[3, 4]
 解析:java做了一個偷懶的拷貝動作,Object類提供的方法clone只是拷貝對象本身,其對象內部的數組、
      引用對象等都不拷貝,還是指向原生對象的內部元素地址,這種拷貝叫做淺拷貝,
      多個對象共享一個變量,是一種非常不安全的方式。內部的數組和引用對象不拷貝,
      其他原始類型,比如int、long、char等都會被拷貝,但是對于string類型,
      java希望你認為它是基本類型,它是沒有clone方法的,并且處理機制也比較特殊,
      通過字符串池(stringpool)在需要的時候才在內存中創建新的字符串。
 注:在使用原型模式的時候,引用的成員變量必須滿足,一是類的成員變量,
    不是方法內變量,二必須是可變的引用對象,而不是一個原始的類型或不可變對象。
    對于深拷貝不能使用final進行修飾,因為要重新賦值,對于淺拷貝因為指向的是
    內存中同一個地址,所以無影響。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容