原型模式Prototype

原型模式定義

用原型實例指定建立對象的種類,而且經過拷貝這些原型建立新的對象java

原型模式類圖

類圖說明

  • Client:客戶端用戶。
  • Prototype:抽象類或者接口,聲明具有Clone能力。
  • ConcretePrototype:具體的原型類。

原型模式的優勢

  • 性能優良:
    • 原型模式是在內存二進制流的拷貝,要比直接new一個對象性能好不少,特別是要在一個循環體內產生大量的對象時,原型模式能夠更好地體現其優勢。
  • 逃避構造函數的約束:
    • 這既是它的優勢也是缺點,直接在內存中拷貝,構造函數是不會執行的。所以,若是在構造函數中須要一些特殊的初始化操做的類型,在使用Cloneable實現拷貝時,須要注意構造函數不會執行的問題。

原型模式的使用場景

一、類初始化須要消化很是多的資源,這個資源包括數據、硬件資源等,經過原型拷貝避免這些消耗。數組

二、經過new產生一個對象須要很是繁瑣的數據準備或訪問權限,這時可使用原型模式。ide

三、一個對象須要提供給其餘對象訪問,並且各個調用者可能都須要修改其值,同時有可能每一個對象中有一些共有屬性時,能夠考慮使用原型模式拷貝多個對象提供調用者使用,即保護性拷貝。函數

PS:須要注意的是,經過實現Cloneable接口的原型模式在調用clone函數構造實例時並不必定比經過new操做速度快,只有當經過new構造對象較爲耗時或者說成本較高時,經過clone方法才能得到效率上的提高。所以,在使用Cloneable時須要考慮構建對象的成本以及作一些效率上的測試。固然,實現原型模式也不必定非要實現Cloneable,也有其餘的實現方式。性能

原型模式的簡單實現

// 簡單的可拷貝對象
public class Thing implements Cloneable {
    // 構造函數,在拷貝對象時不會被調用
    public Thing() {
        System.out.println("構造函數被執行了...");
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Thing thing = null;
        try {
            thing = (Thing) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return thing;
    }
}

// 簡單的場景類
public class Client {
    public static void main(String[] args) {
        // 產生一個對象
        Thing thing = new Thing();
        // 拷貝一個對象
        Thing cloneThing = thing.clone();
    }
}

運行結果:測試

構造函數被執行了...

淺拷貝

Object類提供的方法clone只是拷貝本對象(和其中的全局原始類型,好比int、long、char等,還有String),其對象內部的數組、引用對象等都不拷貝,仍是指向原生對象的內部元素地址。this

淺拷貝的例子:spa

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Thing thing = null;
        try {
            thing = (Thing) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return thing;
    }
    
    //設置arrayList的值
    public void setValue(String value) {
        this.arrayList.add(value);
    }
    
    // 取得arrayList的值
    public ArrayList<String> getValue() {
        return this.arrayList;
    }
}

public class Client {
    public static void main(String[] args) {
        // 產生一個對象
        Thing thing = new Thing();
        // 設置一個值
        thing.setValue("張三");
        // 拷貝一個對象
        Thing cloneThing = thing.clone();
        cloneThing.setValue("李四");
        System.out.println(thing.getValue());
    }
}

運行結果:code

[張三, 李四]

由於經過clone拷貝出來的cloneThing對象中的arrayList沒有被單獨拷貝,和thing對象共用一份arrayList,因此改變了cloneThing對象中的arrayList也就改變了thing對象中的arrayList。對象

深拷貝

進行深度的徹底拷貝。兩個對象之間沒有任何的瓜葛了。

深拷貝的例子

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Thing thing = null;
        try {
            thing = (Thing) super.clone();
            // 對arrayList也進行拷貝
            thing.arrayList = (ArrayList<String>) this.arrayList.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return thing;
    }
    
    //設置arrayList的值
    public void setValue(String value) {
        this.arrayList.add(value);
    }
    
    // 取得arrayList的值
    public ArrayList<String> getValue() {
        return this.arrayList;
    }
}

public class Client {
    public static void main(String[] args) {
        // 產生一個對象
        Thing thing = new Thing();
        // 設置一個值
        thing.setValue("張三");
        // 拷貝一個對象
        Thing cloneThing = thing.clone();
        cloneThing.setValue("李四");
        System.out.println(thing.getValue());
    }
}

運行結果:

[張三]

由於對arrayList也進行了獨立的拷貝,因此修改cloneThing對象中的arrayList並不會影響thing對象中的arrayList值。

clone與final

對象的clone與對象內的final關鍵字是有衝突的。好比上面的例子中,若是arrayList有final屬性,在clone處,編譯器會報錯。

因此,要使用clone方法,類的成員變量上不要增長final關鍵字。

Android中的應用

Intent的clone方法,使用的拷貝構造函數的方式來實現。其內部並無調用super.clone(),而是調用了new Intent(this)。正如上文提到的,使用clone和new須要根據構造對象的成原本決定,若是對象的構形成本比較高或者構造較爲麻煩,那麼使用clone()函數效率較高,不然可使用new的形式。

@Override
public Object clone() {
    return new Intent(this);
}

/**
 * Copy constructor.
 */
public Intent(Intent o) {
    this.mAction = o.mAction;
    this.mData = o.mData;
    this.mType = o.mType;
    this.mPackage = o.mPackage;
    this.mComponent = o.mComponent;
    this.mFlags = o.mFlags;
    this.mContentUserHint = o.mContentUserHint;
    if (o.mCategories != null) {
        this.mCategories = new ArraySet<String>(o.mCategories);
    }
    if (o.mExtras != null) {
        this.mExtras = new Bundle(o.mExtras);
    }
    if (o.mSourceBounds != null) {
        this.mSourceBounds = new Rect(o.mSourceBounds);
    }
    if (o.mSelector != null) {
        this.mSelector = new Intent(o.mSelector);
    }
    if (o.mClipData != null) {
        this.mClipData = new ClipData(o.mClipData);
    }
}
相關文章
相關標籤/搜索