《Android源碼設計模式解析與實戰》讀書筆記-原型模式分析

介紹

原型模式是一個創建型模式,該模式有一個樣板實例,用戶從這個對象中復制出一個內部屬性一致的對象。多用于創建復雜的或者構造耗時的實例。因為在這種情況下,復制一個已經存在的實例可使程序運行更加高效。

定義

用原型實例制定創建對象的種類,并通過拷貝這些原型創建新的對象。

使用的場景

  • 類初始化需要消耗非常多的資源,包括數據、硬件資源等,通過原型拷貝避免一些消耗。
  • 通過new產生一個對象需要非常繁瑣的數據或訪問權限。
  • 一個對象需要提供給其他對象訪問,而且各個調用者可能需要修改其值時,可以考慮用原型模式拷貝多個對象供調用者使用,即保護性拷貝。

簡單實現

現有文檔對象,包含文字和圖片列表?,F在用戶需要對文檔進行編輯,但是無法確定編輯后的文檔是否采用,此時可以將原始文檔拷貝一份,然后在副本上進行修改。

/**
 * Created by Bowen on 2016-04-23.
 */
public class Doc implements Cloneable {

    private String mText;
    private ArrayList<String> mImages = new ArrayList<>();

    public Doc() {
    }

    public Doc(String mText, ArrayList<String> mImages) {
        this.mText = mText;
        this.mImages = mImages;
    }

    public void addImage(String image){
        mImages.add(image);
    }

    public String getmText() {
        return mText;
    }

    public void setmText(String mText) {
        this.mText = mText;
    }

    public ArrayList<String> getmImages() {
        return mImages;
    }

    public void setmImages(ArrayList<String> mImages) {
        this.mImages = mImages;
    }

    public void show(){
        System.out.println("Text的內容: "+mText);
        int size = mImages.size();

        for (int i = 0; i < size; i++) {
            System.out.println("Image的內容: "+mImages.get(i));
        }

        System.out.println("==========================");
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {

        Doc doc = (Doc) super.clone();
        doc.mImages = mImages;
        doc.mText = this.mText;

        return doc;
    }
}

我們來看客戶端使用:

/**
 * Created by Bowen on 2016-04-23.
 */
public class Test {
    public static void main(String[] args){
        Doc originDoc = new Doc();
        originDoc.addImage("test1");
        originDoc.setmText("文字1");
        originDoc.show();

        try {
            Doc newDoc = (Doc) originDoc.clone();
            newDoc.setmText("文字2");
            newDoc.show();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        originDoc.show();
    }

}

以下是輸出結果:

Text的內容: 文字1
Image的內容: test1
==========================
Text的內容: 文字2
Image的內容: test1
==========================
Text的內容: 文字1
Image的內容: test1
==========================

可以看到newDoc克隆后修改了Text字段,能正確的顯示,而且并沒有影響originDoc的文本內容。

深拷貝 淺拷貝

上述的原型模式知識一個淺拷貝,也成為影子拷貝。實際上,newDoc并不是將原始文檔的所有字段都重新構造了一份,而是引用了originDoc的字段。
A引用B,說明AB兩個對象指向同一個地址,當修改A時,B也會變,修改B同理。修改客戶端代碼:

/**
 * Created by Bowen on 2016-04-23.
 */
public class Test {
    public static void main(String[] args){
        Doc originDoc = new Doc();
        originDoc.addImage("test1");
        originDoc.setmText("文字1");
        originDoc.show();

        try {
            Doc newDoc = (Doc) originDoc.clone();
            newDoc.setmText("文字2");
            newDoc.addImage("test2");
            newDoc.show();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        originDoc.show();
    }
}

在newDoc中添加衣服圖片。結果顯示:

Text的內容: 文字1
Image的內容: test1
==========================
Text的內容: 文字2
Image的內容: test1
Image的內容: test2
==========================
Text的內容: 文字1
Image的內容: test1
Image的內容: test2
==========================

可以看到,我們在newDoc中添加的圖片,同樣出現在了originDoc中。這是因為上文Doc中clone方法知識簡單的進行淺拷貝,引用類型的新對象newDoc的mImages只是指向了originDoc.mImages引用,并沒有重新構造一個Images對象,所以當我們在newDoc中添加圖片時,導致了originDoc的mImages與originDoc中的是同一個對象,因此,修改任意一個,另一個文檔也會受到影響。

解決問題就是采用深拷貝,再拷貝對象時,對于引用型的字段也要采用拷貝的形式,而不是單純的引用形式,修改clone方法:

@Override
protected Object clone() throws CloneNotSupportedException {

    Doc doc = (Doc) super.clone();
    doc.mImages = (ArrayList<String>) this.mImages.clone();
    doc.mText = this.mText;

    return doc;
}

再次運行,結果如下:

Text的內容: 文字1
Image的內容: test1
==========================
Text的內容: 文字2
Image的內容: test1
Image的內容: test2
==========================
Text的內容: 文字1
Image的內容: test1
==========================

滿足預期結果。

總結

原型模式本質上是對象拷貝,與C++中的拷貝構造函數有些類似。使用原型模式可以

  • 解決構造復雜對象的資源消耗問題,能夠在某些場景提升創建對象的效率。
  • 保護性拷貝,是某個對象對外是只讀的,通常可以通過返回一個對象拷貝的形式實現只讀的限制。
  • 優點
    1. 原型模式是在內存中二進制流的拷貝,比直接new一個對象性能好很多,特別是要在循環體內產生大量對象時,原始模式可以更好的體現其優點。
  • 缺點
    1. 在內存中拷貝,構造函數時不會執行的。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容