程序運(yùn)行更高效-原型模式

模式介紹

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

應(yīng)用場景

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

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

簡單實(shí)例

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

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

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

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

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實(shí)現(xiàn)了Cloneable接口并重寫了clone()方法,在clone()中進(jìn)行對象的克隆操作。
那么接下來我們來實(shí)際演示下克隆效果:

//創(chuàng)建對象
WordDocument document = new WordDocument();
document.setAuthor("孟遠(yuǎn)");
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)建了一篇文章,之后拷貝了這篇文章并修改了拷貝文章。
在這期間,一共進(jìn)行了4次對象的toString

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

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


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

深拷貝

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

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

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

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

可以發(fā)現(xiàn)我們直接引用了當(dāng)前對象的List,導(dǎo)致在內(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;
        //關(guān)建行
        document.imageList = (ArrayList<String>) imageList.clone();
        return document;
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }
    return null;
}

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


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

總結(jié)

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

感謝

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

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

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

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