程序運行更高效-原型模式

模式介紹

原型模式是一種創(chuàng)建式模式。
用戶可以從一個樣板對象中復制出一個內(nèi)部屬性一致的對象,這個過程也就是我們常說的“克隆”。
被復制出的對象就是我們所說的“原型”,這個“原型”是可以進行定制的。
原型模式多用于創(chuàng)建復雜、構造耗時的對象,因為在這種情況下,利用原型模式復制一個已經(jīng)存在的對象可以使程序運行更高效

應用場景

  • 一個對象初始化時需要消耗非常多的資源;
  • 一個對象需要提供給多個對象訪問,并且多個對象調(diào)用時需要進行定制。

需要注意的是,復制操作并不一定比new快,只有當new對象較為耗時或成本較高時,通過復制才能獲得效率上的提升。
因此,在使用原型模式時需要對對象的構建成本進行一些效率測試。

簡單實例

如何實現(xiàn)復制操作?
非常簡單,Java提供了一個叫做Cloneable的接口,我們只需實現(xiàn)該接口,重寫它的clone()方法即可。
現(xiàn)在我們來舉個簡單實例來演示原型模式
假設我們現(xiàn)在有一篇文檔對象:

public class WordDocument{
    //文本
    private String text;
    //作者
    private String author;
    //圖片集合
    private ArrayList<String> imageList;

    //....省略get、set
}

里面包含了作者、文本以及圖片合集,現(xiàn)在我們要讓這個文檔實現(xiàn)復制功能:

public class WordDocument implements Cloneable {
    //文本
    private String text;
    //作者
    private String author;
    //圖片集合
    private ArrayList<String> imageList;
    
    @Override
    protected WordDocument clone() {
        WordDocument document = null;
        try {
            document = (WordDocument) super.clone();
            document.text = this.text;
            document.author = this.author;
            document.imageList = this.imageList;
            return document;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
    
    //....省略get、set
}

非常簡單,我們讓WordDocument實現(xiàn)了Cloneable接口并重寫了clone()方法,在clone()中進行對象的克隆操作。
那么接下來我們來實際演示下克隆效果:

//創(chuàng)建對象
WordDocument document = new WordDocument();
document.setAuthor("孟遠");
document.setText("這是一篇極好的文章。");
document.addImage("圖0");
document.addImage("圖1");
//打印創(chuàng)建的對象
tv_clone_0.append("創(chuàng)建的對象:\n" + document.toString());
//克隆對象
WordDocument cloneBean = document.clone();
//打印克隆的對象
tv_clone_0.append("克隆的對象:\n" + cloneBean.toString());
//修改克隆的對象
cloneBean.setAuthor("黑色小老虎");
cloneBean.addImage("新加圖片2");
//再次打印2個對象
tv_clone_0.append("修改克隆對象:\n" + cloneBean.toString());
tv_clone_0.append("最開始的對象:\n" + document.toString());

這段代碼我們創(chuàng)建了一篇文章,之后拷貝了這篇文章并修改了拷貝文章。
在這期間,一共進行了4次對象的toString

  1. 創(chuàng)建對象完成時打印了創(chuàng)建對象;
  2. 拷貝完成時打印了拷貝對象;
  3. 修改拷貝對象完成時打印了拷貝對象;
  4. 最后再次打印最初創(chuàng)建的對象。

這里請注意,我們修改拷貝對象時,修改了作者姓名并且添加了一張圖片。
最后打印的結果如下:


細心的同學會發(fā)現(xiàn),我們修改了拷貝對象的作者昵稱,原對象沒有受到影響。但是我們增加一張圖片到拷貝對象的集合中時,原對象也發(fā)生了變化。
這就牽扯到了淺拷貝深拷貝。

深拷貝

上述簡單實例,是使用了淺拷貝來實現(xiàn)的。所謂淺拷貝就是直接引用原對象中的嵌套對象,不會去進行創(chuàng)建。
大家應該都知道對象引用的問題:

Bean a = new Bean("孟遠","23","男");
Bean b = a;

此時b對象引用了a對象,也就是說其實a和b兩個對象在堆內(nèi)存中指向的是同一個地址,當修改b時,a也必定跟著發(fā)生變化。
同理我們再回頭重新看下WordDocumentclone()代碼:

 @Override
 protected WordDocument clone() {
     WordDocument document = null;
     try {
         document = (WordDocument) super.clone();
         document.text = this.text;
         document.author = this.author;
         //問題所在,直接引用當前對象的List
         document.imageList = this.imageList;
         return document;
     } catch (CloneNotSupportedException e) {
         e.printStackTrace();
     }
     return null;
 }

可以發(fā)現(xiàn)我們直接引用了當前對象的List,導致在內(nèi)存中拷貝對象和最初對象的List指向了同一個地址。
上面代碼就是我們口中的淺拷貝。
那么如何解決這個問題?
很簡單,使用深拷貝即內(nèi)部對象也使用clone()

@Override
protected WordDocument clone() {
    WordDocument document = null;
    try {
        document = (WordDocument) super.clone();
        document.text = this.text;
        document.author = this.author;
        //關建行
        document.imageList = (ArrayList<String>) imageList.clone();
        return document;
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }
    return null;
}

我們點進ArrayList的源碼可以發(fā)現(xiàn),ArrayList已經(jīng)實現(xiàn)了Cloneable接口,所以我們直接調(diào)用ArrayList的clone()即可。
修改完這一行代碼之后,再次執(zhí)行上面演示代碼:


完美,可以發(fā)現(xiàn)在修改完克隆對象之后,最開始的對象已經(jīng)不會受到影響。

總結

上述演示代碼已經(jīng)上傳至GitHub。
原型模式是非常簡單的一個模式,它的核心問題就是對原始對象進行拷貝,在這個模式的使用過程中需要注意一點就是:深、淺拷貝的問題。
在實際開發(fā)過程中,為了減少錯誤,建議各位讀者在使用原型模式時盡量使用深拷貝,避免操作副本時影響到原始對象。

感謝

《Android源碼設計模式解析與實戰(zhàn)》 何紅輝、關愛民 著

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容