原型模式——使程序運行更高效

(《設計模式解析與實戰》讀書筆記)

一、定義
用原型實例指定創建對象的種類,並通過拷貝這些原型創建新的對象;
二、使用場景
(1)類初始化需要消化非常多的資源,這個資源包括數據、硬件資源等,通過原型拷貝避免這些消耗;
(2)通過new產生一個對象需要非常繁瑣的數據準備或訪問權限,這時可使用原型模式;
(3)一個對象需要提供給其他對象訪問,而且各個調用者可能都需要修改其值時,可以考慮使用原型模式拷貝多個對象供調用者使用,即保護性拷貝。
三、簡單示例
修改一份文檔,我們一般不會直接修改“原型”,而是拷貝一份副本進行修改。

/**
 * 文檔是具體的原型類;Cloneable是接口,聲明具備clone能力
 */
public class WordDocument implements Cloneable {
    // 文本
    private String mText;
    // 圖片列表
    private ArrayList<String> mImages = new ArrayList<>();

    public WordDocument() {
        System.out.println("------我是WordDocument構造函數-------");
    }

    @Override
    protected WordDocument clone() {
        try {
            WordDocument doc = (WordDocument) super.clone();
            doc.mText = this.mText;
            doc.mImages = this.mImages;
            return doc;
        } catch (Exception e) {
        }
        return null;
    }

    public String getmText() {
        return mText;
    }

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

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

    public void addImages(String img) {
        this.mImages.add(img);
    }

    /**
     * 打印文檔內容
     */
    public void showDocument() {
        System.out.println("-----開始打印-----");
        System.out.println("Text :" + mText);
        System.out.println("Images List : ");
        for (String imgName : mImages) {
            System.out.println("image name : " + imgName);
        }
        System.out.println("-----打印結束-----");
    }

}
public class Client {
    public static void main(String[] args) {
        // 1、構建文檔對象
        WordDocument document = new WordDocument();
        // 2、修改文檔
        document.setmText("這是一篇文檔");
        document.addImages("圖片1");
        document.addImages("圖片2");
        document.showDocument();

        // 以原始文檔爲原型,拷貝一份副本
        WordDocument document2 = document.clone();
        System.out.println("這是未修改的副本");
        document2.showDocument();

        // 修改文檔副本,不會影響原始文檔
        document2.setmText("這是修改後的副本");
        document2.showDocument();

        // 驗證原始文檔是否被修改
        document.showDocument();
    }
}

輸出後的結果:

這裏寫圖片描述
這裏寫圖片描述
可以看出在副本上修改不會影響到原型。並且通過clone拷貝對象時並不會執行構造函數。
修改Client代碼:
// 修改文檔副本

        document2.setmText("這是修改後的副本");
        document2.addImages("副本圖片.jpg");
        document2.showDocument();

運行結果:
這裏寫圖片描述
會發現原型被修改了。原因是上述方法只是淺拷貝,document2的mImages只是單純的指向了this.mImages引用,並沒有重新構造一個mImages對象。
運用深拷貝可解決這個問題,在拷貝時,對於引用型的字段也要採用拷貝的形式,而不是引用的形式。
修改clone()方法:

    @Override
    protected WordDocument clone() {
        try {
            WordDocument doc = (WordDocument) super.clone();
            doc.mText = this.mText;
            // 對mImages對象調用clone()函數,進行深拷貝
            doc.mImages = (ArrayList<String>) this.mImages.clone();
            return doc;
        } catch (Exception e) {
        }
        return null;
    }

運行後發現副本添加圖片並不會影響到原型。
這裏寫圖片描述
所以在使用該模式時儘量使用深拷貝,避免操作副本時影響原始對象的問題。

四、優缺點
優點:
運行模式是在內存中二進制流的拷貝,要比直接new一個對象性能好很多,特別是要在一個循環體內產生大量的對象時,原型模式可以更好的體現其優點。
缺點:
直接在內存中拷貝,構造函數是不會執行的,在實際開發中應該注意這個潛在的問題。優點是減少了約束,缺點也是減少了約束,需要我們在實際應用中考慮。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章