設計模式之原型模式【Prototype Pattern】

原型模式是創建型模式,主要用來對原始對象進行拷貝。

1、定義

用原型實例指定創建對象的種類,並通過複製這些原型來創建新的對象。

2、使用場景

  1. 類初始化需要消耗非常多的資源,資源包括數據、硬件資源等,通過原型複製避免資源消耗。
  2. 通過 new 生成對象需要非常繁瑣的數據準備或訪問權限。
  3. 一個對象需要提供給其他對象訪問,但是其他對象可能對其修改時。可以使用原型模式複製多個對象供調用者使用。即保護性拷貝。

具體實現時,使用 new 還是 Cloneable的 clone() 要根據實際情況來定:當通過 new 構造對象較爲耗時或者成本較高時,通過 clone 方法才能獲得效率上的提升。

3、UML圖

這裏寫圖片描述

  1. Prototype:抽象類或接口,申明一個克隆自身的接口。
  2. ConcretePrototype :具體的原型類
  3. Client:調用者

4、示例代碼

1. 通過自定義接口實現

/**
 * 申明一個克隆自身的接口
 */
public interface Prototype {

    public Prototype clone();

}

public class ConcretePrototype implements Prototype {

    //真正實現克隆自身的方法
    @Override
    public Prototype clone() {
        ConcretePrototype prototype = new ConcretePrototype();
        return prototype;
    }
}

public class Client {

    Prototype prototype;

    public Client(Prototype p) {
        prototype = p;
    }

    public void operation() {
        //克隆一個newPrototype
        Prototype newPrototype = prototype.clone();
    }
}

2. 通過實現java的 Cloneable 接口實現
這種方式只需要實現 Cloneable 接口,重寫 clone() 就可以了。

public class ConcretePrototype implements Cloneable {

    @Override
    protected ConcretePrototype clone() throws CloneNotSupportedException {
        return (ConcretePrototype) super.clone();
    }
}

5、淺拷貝和深拷貝

public class ConcretePrototype implements Cloneable {

    private String str;
    private List<String> list = new ArrayList<String>();

    public String getStr() {
        return str;
    }

    public void setStr(String str) {
        this.str = str;
    }

    public List<String> getList() {
        return list;
    }

    public void addString(String str) {
        this.list.add(str);
    }

    @Override
    protected ConcretePrototype clone() throws CloneNotSupportedException {
        ConcretePrototype p = (ConcretePrototype) super.clone();
        p.str = this.str;
        p.list = this.list;
        return p;
    }
}

對於以上的這個實現,其實是是一個淺拷貝(影子拷貝),並沒有將原始對象的所有字段都重新構造一份,而是直接指向了原始對象字段的引用,也就是拷貝對象的字段引用了原始對象的字段。它們指向的是同一個對象。在修改拷貝對象的list時,會對原始對象的list造成影響。那該如何寫纔不會造成影響呢?

答案就是採用深拷貝的寫法,在拷貝對象時,對於引用型的字段也要採用拷貝的形式,而不是單純的引用形式。clone( ) 修改如下:

@Override
protected ConcretePrototype2 clone() throws CloneNotSupportedException {
    ConcretePrototype2 p = (ConcretePrototype2) super.clone();
    p.str = this.str;
    //p.list = this.list;
    //對於list對象,也調用 clone() 進行拷貝
    p.list = (ArrayList<String>) this.list.clone();
    return p;
}

原型模式的核心問題就是對原始對象進行拷貝,拷貝時要注意深、淺拷貝問題。
在開發過程中,爲減少錯誤,應儘量使用深拷貝,避免對原始對象造成影響。

6、Android源碼中實現

Android 中 Intent 類就實現了原型模式。

public class Intent implements Parcelable, Cloneable {
    /**
     * 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);
        }
    }

    @Override
    public Object clone() {
        return new Intent(this);
    }
    //省略其他代碼
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章