Android設計模式之(3)----原型模式

原型模式


用原型實例指定創建對象的,拷貝這些對象生成新的對象進行使用。

也可以直接進行new一個對象,但是當對象的構造複雜時,new的效率會很低,使用clone更好

new適用於簡單的構造

clone適用於複雜的構造

應用場景

  • 資源消耗大的對象
  • 節省資源,比如在for循環內創建相同的對象
  • 一個對象要讓其他人使用,並且使用過程中要改變這個對象的某些屬性,可考慮將原有對象拷貝,並且修改需要改變的屬性,在進行使用
  • 對象結構複雜的情況使用拷貝

淺拷貝代碼實現

原型對象實現Cloneable接口默認是淺拷貝

class Prototype implements Cloneable {

    public Prototype clone() {
        Prototype prototype = null;
        try {
            prototype = (Prototype) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return prototype;
    }


}

原型對象具體的內容實現

class ConcretePrototype extends Prototype {

    public String name;
    public ArrayList<String> list = new ArrayList<>();

    public ConcretePrototype() {
        System.out.println("執行了ConcretePrototype構造函數");
    }

    @Override
    public String toString() {
        return "ConcretePrototype{" +
                "name='" + name + '\'' +
                ", list=" + list +
                '}';

    }


    public void show() {
        System.out.println("原型模式實現類");
    }
}   

使用方式

  ConcretePrototype cp = new ConcretePrototype();
        cp.name = "原始數據";
        for (int i = 0; i < 10; i++) {
            cp.list.add(i, String.valueOf(i));//填充數據
        }
        ConcretePrototype cloneConcretePrototype = (ConcretePrototype) cp.clone();//實現拷貝
        cloneConcretePrototype.name = "拷貝數據";
        System.out.println(cp.toString());
        System.out.println(cloneConcretePrototype.toString());

顯示結果:

執行了ConcretePrototype構造函數
ConcretePrototype{name='原始數據', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
ConcretePrototype{name='拷貝數據', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}

原型對象不實現Cloneable接口默認是淺拷貝

其實就是把cloneable接口的內容手動實現一次

class ConcretePrototype2 {

    public String name;
    public ArrayList<String> list = new ArrayList<>();

    public ConcretePrototype2() {
        System.out.println("執行了ConcretePrototype2構造函數");
    }

    public ConcretePrototype2 clone() {
       //手動new對象進行賦值
        concretePrototype2 concretePrototype2 = new ConcretePrototype2();
        concretePrototype2.name = this.name;
        concretePrototype2.list = this.list;
        return concretePrototype2;
    }


    @Override
    public String toString() {
        return "ConcretePrototype2{" +
                "name='" + name + '\'' +
                ", list=" + list +
                '}';

    }


    public void show() {
        System.out.println("原型模式實現類");
    }
}

調用方式

 ConcretePrototype2 cp = new ConcretePrototype2();
        cp.name = "原始數據";
        for (int i = 0; i < 10; i++) {
            cp.list.add(i, String.valueOf(i));//填充數據
        }
        ConcretePrototype2 cloneConcretePrototype = (ConcretePrototype2) cp.clone();//實現拷貝
        cloneConcretePrototype.name = "拷貝數據";
        System.out.println(cp.toString());
        System.out.println(cloneConcretePrototype.toString());

顯示結果

執行了ConcretePrototype2構造函數
執行了ConcretePrototype2構造函數
ConcretePrototype2{name='原始數據', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
ConcretePrototype2{name='拷貝數據', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}

構造函數執行了兩次,說明了cloneable接口的複製是不會執行2次構造的,直接new對象則會調用2次構造。

測試

修改複製後的數據

 ConcretePrototype2 cp = new ConcretePrototype2();
        cp.name = "原始數據";
        for (int i = 0; i < 10; i++) {
            cp.list.add(i, String.valueOf(i));//填充數據
        }
        ConcretePrototype2 cloneConcretePrototype = (ConcretePrototype2) cp.clone();//實現拷貝
        cloneConcretePrototype.name = "拷貝數據";
        cloneConcretePrototype.list.add("追加測試數據");
        System.out.println(cp.toString());
        System.out.println(cloneConcretePrototype.toString());

顯示結果

執行了ConcretePrototype2構造函數
執行了ConcretePrototype2構造函數
ConcretePrototype2{name='原始數據', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 追加測試數據]}
ConcretePrototype2{name='拷貝數據', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 追加測試數據]}

【追加測試數據無論是原型還是拷貝後的對象都有這個數據】

問題原因:深拷貝-淺拷貝

Cloneable接口默認實現的是淺拷貝,對於基本類型會進行拷貝,但是對於引用類型(集合,數據,對象)等clone僅僅代表指向了同一個內存地址,所以修改一個兩個都會變化。

解決方案:在引用類型的具體數據類型在進行一次clone,將淺拷貝處理爲深拷貝

深拷貝代碼實現

代碼如下

  public ConcretePrototype2 clone() {
        //手動new對象進行賦值
//        ConcretePrototype2 concretePrototype2 = new ConcretePrototype2();
//        concretePrototype2.name = this.name;
//        concretePrototype2.list = this.list;
//        return concretePrototype2;

        //將具體的集合修改爲深拷貝
        ConcretePrototype2 concretePrototype2 = new ConcretePrototype2();
        concretePrototype2.name = this.name;
        concretePrototype2.list = (ArrayList<String>) this.list.clone();

        return concretePrototype2;

    }

顯示結果

執行了ConcretePrototype2構造函數
執行了ConcretePrototype2構造函數
ConcretePrototype2{name='原始數據', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
ConcretePrototype2{name='拷貝數據', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 追加測試數據]}

應證我們的想法,對於集合,數組,對象等,如果要新增數據,需要對其重寫更改具體的數據類型。

總結

原型模式可以避免構造複雜的對象時的資源消耗問題,提升創建對象的效率。
可以保護原始對象的數據安全性
clone是二進制流,對於複雜的構造對象,性能提升明顯,但是對於簡單的構造沒必要使用clone

github代碼地址

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