Android設計模式之原型模式

原型模式

1.定義:

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

2.使用場景:

  • 類初始化需要消耗非常多的資源,這個資源包括數據、硬件資源等,通過原型copy避免了這些消耗;
  • 通過new產生一個對象需要非常繁瑣的數據準備或訪問權限,這時可以使用原型模式;
  • 一個對象需要提供給其他對象訪問,而且各個調用者可能都需要修改其值時,可以考慮使用原型模式copy多個對象供調用者使用,即保護性copy。
    注意:
    1.通過實現Cloneable接口的原型模式再調用clone函數構造實例時,并不一定比通過new操作效速度快,只有當通過new構造對象較為耗時或成本較高時,通過clone方法才能獲得效率上的提升。因此,在使用Cloneable時需要考慮構建對象的成本以及一些效率上的測試。
    2.實現原型模式不一定非要實現Cloneable接口,也有其他的實現方式。

3.UML圖

4.詳解:

原型模式是一個創建型模式,‘原型’二字表明該模式應該有一個樣板實例,提供給用戶訪問copy。用戶從這個樣板對象中復制出一個內部屬性一致的對象的過程,稱為克隆。被復制的樣板對象就是‘原型’。原型模式多用于創建復雜的或構造耗時的實例,因為這些情況下,復制一個已經存在的實例可使程序運行效率更高。
下面用代碼舉例闡述:

public static class WordDocument implements Cloneable {
        private String text;
        private ArrayList<String> images = new ArrayList<>();

        public WordDocument() {
            System.out.println("constructor start");
        }

        @Override
        protected WordDocument clone() {
            try {
                WordDocument doc = (WordDocument) super.clone();
                doc.text = text;
                doc.images = images;//淺拷貝
                return doc;
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
                return null;
            }
        }

        public String getText() {
            return text;
        }

        public void setText(String text) {
            this.text = text;
        }

        public List<String> getImages() {
            return images;
        }

        public void addImage(String img) {
            images.add(img);
        }

        public void showDocument() {
            System.out.println("text:" + text);
            System.out.print("images:");
            for (int i = 0; i < images.size(); i++) {
                if (i == images.size() - 1) {
                    System.out.print(images.get(i) + "\n");
                } else {
                    System.out.print(images.get(i) + ",");
                }
            }
        }
    }

測試代碼:

public static void main(String[] args) {
        WordDocument originDoc = new WordDocument();
        originDoc.setText("原文檔text");
        originDoc.addImage("img1");
        originDoc.addImage("img2");
        originDoc.addImage("img3");
        originDoc.showDocument();
        System.out.println("=========================");

        WordDocument newDoc = originDoc.clone();
        newDoc.showDocument();
        System.out.println("=========================");

        newDoc.setText("修改后的text");
        newDoc.addImage("img4");
        newDoc.showDocument();

        System.out.println("=========================");
        originDoc.showDocument();
        /**
         * 輸出結果:
         constructor start
         text:原文檔text
         images:img1,img2,img3
         =========================
         text:原文檔text
         images:img1,img2,img3
         =========================
         text:修改后的text
         images:img1,img2,img3,img4
         =========================
         text:原文檔text
         images:img1,img2,img3,img4
         */
    }

這里的WordDocument 就是所謂的‘原型’,它里面有文字描述與圖片url。現在用戶需要重新編輯該文檔,又想不破壞原文檔,于是用戶需要復制一份原文檔,在拷貝文檔上編輯,詳細過程見測試代碼。
從上面的測試結果可以得出如下結論:

  • 通過clone 拷貝對象時,并不會執行構造函數,因此,如果在構造函數中需要初始化操作的類型,在使用Cloneable實現拷貝時,需注意構造函數不會執行的問題。
  • 副文檔修改了文本內容,原文檔內容不受影響,這樣保證了原文檔的安全性;
  • 但是,細心的你也許發現了,在副文檔中添加img4,原文檔也會有img4。說好的安全呢?瞬間被打臉,有木有?其實這個例子是淺拷貝(也稱為‘影子拷貝’),這份拷貝并不是將原始文檔的所有字段都重構了一份,而是副文檔的字段引用了原始文檔的字段,即原文檔與副文檔的字段images其實指向同一個地址,于是才會出現上述情況。那么這種情況該如何規避?答案是深拷貝!即在拷貝對象時,對于引用型的字段也采用拷貝的形式,而不是上面的單純引用的方式。
    下面修改clone方法:doc.images = (ArrayList<String>) images.clone(),即深拷貝。
    @Override
        protected WordDocument clone() {
            try {
                WordDocument doc = (WordDocument) super.clone();
                doc.text = text;
                doc.images = (ArrayList<String>) images.clone();//深拷貝
                return doc;
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
                return null;
            }
        }

測試代碼不變,但是輸出的結果卻變了,如下:

        /**
         * 輸出結果:
         constructor start
         text:原文檔text
         images:img1,img2,img3
         =========================
         text:原文檔text
         images:img1,img2,img3
         =========================
         text:修改后的text
         images:img1,img2,img3,img4
         =========================
         text:原文檔text
         images:img1,img2,img3
         */

修改了副文檔的images,原文檔也沒有改變,很安全了。
原型模式是非常簡單的模式,它的核心問題就是對原始對象進行拷貝,在這個模式的使用過程中需要注意的一點就是深、淺拷貝的問題。在實際開發過程中,為了減少錯誤,盡量使用深拷貝原型模式,避免操作副本對象時,影響了原始對象的屬性

5.代碼托管地址

原型模式

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容