Android設計模式之——原型模式

一、介紹

原型模式是一個創建型的模式。原型二字表明了該模型應該有一個樣板實例,用戶從這個樣板對象中復制出一個內部屬性一致的對象,這個過程也就是我們俗稱的“克隆”。被復制的實例就是我們所稱的“原型”,這個原型也是可定制的。原型模型多用于創建復雜的或者構造耗時的實例,因為這種情況下,復制一個已經存在的實例可使程序運行更高效。

二、定義

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

三、使用場景

  • (1)類初始化需要消耗非常多的資源,這個資源包括數據、硬件資源等,通過原型拷貝避免這些消耗。

  • (2)通過new產生一個對象需要非常繁瑣的數據準備或訪問權限,這時可以使用原型模式。

  • (3)一個對象需要提供給其他對象訪問,而且各個調用者可能都需要修改其值時,可以考慮使用原型模式拷貝多個對象供調用者使用,即保護性拷貝。

需要注意的是,通過實行Cloneable接口的原型模式在調用clone函數構造實例時并不一定比通過new操作速度快,只有當通過new構造對象較為耗時或者說成本較高時,通過clone方法才能夠獲得效率上的提升。因此,在使用Cloneable時需要考慮構建對象的成本以及做一些效率上的測試。當然,實現原型模式也不一定非要實現Cloneable接口,也有其他的實現方式,這里將會對這些一一說明。

四、原型模型的UML類圖

圖1

圖中角色介紹:

  • Client:客戶端用戶。

  • Prototype:抽象類或者接口,聲明具備clone能力。

  • ConcretePrototype:具體的原型類。

五、原型模式的簡單實現

下面以簡單的文檔拷貝為例來演示一下簡單的原型模式,我們在這個例子中首先創建了一個文檔對象,即WordDocument,這個文檔中含有文字和圖片。用戶經過了長時間的內容編輯后,打算對該文檔做進一步的編輯,但是,這個編輯后的文檔是否會被采用還不確定,因此,為了安全起見,用戶需要將當前文檔拷貝一份,然后再在文檔副本上進行修改,這與《Effective Java》一書中提到的保護性拷貝有些類似,如此,這個原始文檔就是我們上述所說的樣板實例,也就是將要被“克隆”的對象,我們成為原型:

示例代碼:

/*** 文檔類型,扮演的是ConcretePrototype角色,而cloneable是代表prototype角色*/
public class WordDocument implements Cloneable {//文本private String mText;//圖片名列表private ArrayList<String> mImages = new ArrayList<String>();public WordDocument(){System.out.println("-------- WordDocument構造函數 --------");}public String getText(){return this.mText;}public void setText(String text){this.mText = text;}public ArrayList<String> getImages(){return this.mImages;}public void setImages(ArrayList<String> images){this.mImages = images;}public void addImage(String img){this.mImages.add(img);}/*** 打印文檔*/public void showDocument(){System.out.println("-------- Word Content Start --------");System.out.println("Text : " + this.mText);System.out.println("Images List : ");for(String image : mImages){System.out.println("image name : " + image);}System.out.println("-------- Word Content End --------");}@Overrideprotected WordDocument clone(){try{WordDocument doc = (WordDocument)super.clone();doc.mText = this.mText;doc.mImages = this.mImages;return doc;}catch(Exception e){}return null;}
}

執行方法:

    public static void main(String[] args) throws IOException {//1.構建文檔對象WordDocument originDoc = new WordDocument();//2.編輯文檔,添加圖片等originDoc.setText("這是一篇文檔");originDoc.addImage("圖片一");originDoc.addImage("圖片二");originDoc.addImage("圖片三");originDoc.showDocument();//以原始文檔為原型,拷貝一份副本WordDocument doc2 = originDoc.clone();doc2.showDocument();//修改文檔副本doc2.setText("這是修改過的Doc2文本");doc2.addImage("這是新添加的圖片");originDoc.showDocument();doc2.showDocument();}

執行結果:

-------- WordDocument構造函數 --------
//originDoc
-------- Word Content Start --------
Text : 這是一篇文檔
Images List : 
image name : 圖片一
image name : 圖片二
image name : 圖片三
-------- Word Content End --------//doc2
-------- Word Content Start --------
Text : 這是一篇文檔
Images List : 
image name : 圖片一
image name : 圖片二
image name : 圖片三
-------- Word Content End --------//副本修改后originDoc
-------- Word Content Start --------
Text : 這是一篇文檔
Images List : 
image name : 圖片一
image name : 圖片二
image name : 圖片三
image name : 這是新添加的圖片
-------- Word Content End --------//副本修改后doc2
-------- Word Content Start --------
Text : 這是修改過的Doc2文本
Images List : 
image name : 圖片一
image name : 圖片二
image name : 圖片三
image name : 這是新添加的圖片
-------- Word Content End --------

這里我們發現通過修改doc2后,只是影響了originDoc的mImages,而沒有改變mText。

六、淺拷貝和深拷貝

上述原型模式的實現實際上只是一個淺拷貝,也稱影子拷貝,這份拷貝實際上并不是將原始的文檔的所有字段都重新構造了一份,而是副本文檔的字段引用原始文檔的字段,如下圖:

這里寫圖片描述

細心的讀者可能從上面的結果中發現,最后兩個文檔信息輸出是一致的。我們在doc2添加了一張圖片,但是,同時也顯示在originDoc中,這是怎么回事呢?學習過C++的讀者都會有比較深刻的體會,這是因為上文中WordDocument的clone方法中只是簡單的進行了淺拷貝,引用類型的新對象doc2.mImages只是單純的指向了this.mImages引用,并沒有重新構造一個mImages對象,然后將原始文檔中的圖片添加到新的mImages對象中,這樣就導致doc2.mImages與原始文檔中的是同一個對象,因此,修改了其中一個文檔中的圖片,另一個文檔也會受影響。那么如何解決這個問題呢?答案就是采用深拷貝,即在拷貝對象時,對于引用型的字段也要采用拷貝的形式,而不是單純引用的形式。

clone方法修改如下(其他不變):

    @Overrideprotected 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;}

修改后在執行上述代碼的結果是:

-------- WordDocument構造函數 --------
//originDoc
-------- Word Content Start --------
Text : 這是一篇文檔
Images List : 
image name : 圖片一
image name : 圖片二
image name : 圖片三
-------- Word Content End --------//doc2
-------- Word Content Start --------
Text : 這是一篇文檔
Images List : 
image name : 圖片一
image name : 圖片二
image name : 圖片三
-------- Word Content End --------//副本修改后originDoc
-------- Word Content Start --------
Text : 這是一篇文檔
Images List : 
image name : 圖片一
image name : 圖片二
image name : 圖片三
-------- Word Content End --------//副本修改后doc2
-------- Word Content Start --------
Text : 這是修改過的Doc2文本
Images List : 
image name : 圖片一
image name : 圖片二
image name : 圖片三
image name : 這是新添加的圖片
-------- Word Content End --------

可以看出現在互不影響,這個叫做深拷貝。

接著上面的疑問,其實String類型在淺拷貝時和引用類型一樣,沒有單獨復制,而是引用同一地址,因為String沒有實現cloneable接口,也就是說只能復制引用。(這里我們可以查看源碼可以看到,而ArrayList實現了cloneable接口)但是當修改其中的一個值的時候,會新分配一塊內存用來保存新的值,這個引用指向新的內存空間,原來的String因為還存在指向他的引用,所以不會被回收,這樣,雖然是復制的引用,但是修改值的時候,并沒有改變被復制對象的值。

所以在很多情況下,我們可以把String在clone的時候和基本類型做相同的處理,只是在equals時注意一些就行了。

原型模式是非常簡單的一個模式,它的核心問題就是對原始對象進行拷貝,在這個模式的使用過程中需要注意的一點就是:深、淺拷貝的問題。在開發過程中,為了減少錯誤,作者建議使用該模式時盡量使用深拷貝,避免操作副本時影響原始對象的問題。

七、Android源碼中的原型模式

示例代碼:

    Uri uri = Uri.parse("smsto:110");Intent intent = new Intent(Intent.ACTION_SEND,uri);intent.putExtra("sms_body", "The SMS text");//克隆Intent intent2 = (Intent)intent.clone();startActivity(intent2);

八、總結

原型模式本質上就是對象的拷貝,與C++中的拷貝構造函數有些類似,它們之間容易出現的問題也都是深拷貝、淺拷貝。使用原型模式可以解決構建復雜對象的資源消耗問題,能夠在某些場景下提升創建對象的效率。

優點:

  • (1)原型模式是在內存中二進制流的拷貝,要比直接new一個對象性能好很多,特別是要在一個循環體內產生大量對象時,原型模式可能更好的體現其優點。

  • (2)還有一個重要的用途就是保護性拷貝,也就是對某個對象對外可能是只讀的,為了防止外部對這個只讀對象的修改,通常可以通過返回一個對象拷貝的形式實現只讀的限制。

缺點:

  • (1)這既是它的優點也是缺點,直接在內存中拷貝,構造函數是不會執行的,在實際開發中應該注意這個潛在問題。優點是減少了約束,缺點也是減少了約束,需要大家在實際應用時考慮。

  • (2)通過實現Cloneable接口的原型模式在調用clone函數構造實例時并不一定比通過new操作速度快,只有當通過new構造對象較為耗時或者說成本較高時,通過clone方法才能夠獲得效率上的提升。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/447031.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/447031.shtml
英文地址,請注明出處:http://en.pswp.cn/news/447031.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C++ primer第六章6.7函數指針

函數指針 函數指針指向的是函數而不是對象。和其他指針一樣&#xff0c;函數指針指向某種特定的類型。函數的類型由他的返回類型和形參類型共同決定&#xff0c;而與函數的名字無關。 //比較兩個string對象的長度 bool lengthCompare(const string &,const string &);…

Android設計模式之——工廠方法模式

一、介紹 工廠方法模式&#xff08;Factory Pattern&#xff09;&#xff0c;是創建型設計模式之一。工廠方法模式是一種結構簡單的模式&#xff0c;其在我們平時開發中應用很廣泛&#xff0c;也許你并不知道&#xff0c;但是你已經使用了無數次該模式了&#xff0c;如Android…

C++ primer第十八章 18.1小結 異常處理

18.1 異常處理 異常處理機制&#xff0c;允許程序獨立開發的部分能夠在運行的時候出現的問題進行通信并且做出相應的處理&#xff0c;異常的處理使得我們可以將問題的檢測和處理分離開來。程序的一部分負責檢測問題的出現&#xff0c;然后將解決這個問題的任務傳遞給程序的另一…

淺談equals與==

一、前言 示例代碼&#xff1a; public static void main(String[] args) throws IOException {String str1 new String("hello");String str2 new String("hello");String str3 "cde";String str4 "cde";int i1 3;int i2 3;In…

針對C++異常的學習

源碼 頭文件 sdf_exception.h #pragma once#include <exception> #include <string>namespace sdf {namespace common{using sdf_error_code_t uint32_t;class SdfException : std::exception{public:explicit SdfException(sdf_error_code_t errorCode) : erro…

Android設計模式之——抽象工廠模式

一、介紹 抽象工廠模式&#xff08;Abstract Factory Pattern&#xff09;&#xff0c;也是創建型設計模式之一。前一節我們已經了解了工廠方法模式&#xff0c;那么這個抽象工廠又是怎么一回事呢&#xff1f;大家聯想一下現實生活中的工廠肯定都是具體的&#xff0c;也就是說…

Android設計模式之——策略模式

一、介紹 在軟件開發中也常常遇到這樣的情況&#xff1a;實現某一個功能可以有多種算法或者策略&#xff0c;我們根據實際情況選擇不同的算法或者策略來完成該功能。例如&#xff0c;排序算法&#xff0c;可以使用插入排序、歸并排序、冒泡排序等。 針對這種情況&#xff0c;…

密碼學在區塊鏈隱私保護中的應用學習

身份隱私保護技術 混淆服務 混淆服務的目的在于混淆消息雙方的聯系&#xff08;如 圖 2 所示&#xff09;。當發送方需要告知接收方消息 M 時&#xff0c; 它會首先用接收方的公鑰 KB 加密 M&#xff0c;并在密文后 附帶真實接收地址 R。為了借助第三方&#xff08;圖 2 中的…

值類型和引用類型的區別

一、定義 引用類型表示你操作的數據是同一個&#xff0c;也就是說當你傳一個參數給另一個方法時&#xff0c;你在另一個方法中改變這個變量的值&#xff0c;那么調用這個方法是傳入的變量的值也將改變。 值類型表示復制一個當前變量傳給方法&#xff0c;當你在這個方法中改變…

面向區塊鏈的高效物化視圖維護和可信查詢論文學習

物化視圖介紹 如何維護物化視圖仍舊是一個開放問題.在關系數據庫中,增量刷新的物化視圖維護策略可劃分為立即維護和延遲維護兩大類.立即維護策略的優點是實現較為簡單,在單數據源下不 存在一致性問題;然而該策略將物化視圖維護過程嵌入到更新事務之中,延長了更新事務的提交時間…

Java基礎知識(一)

一、接口 類描述了一個實體&#xff0c;包括實體的狀態&#xff0c;也包括實體可能發出的動作。 接口定義了一個實體可能發出的動作。但是只是定義了這些動作的原型&#xff0c;沒有實現&#xff0c;也沒有任何狀態信息。 所以接口有點象一個規范、一個協議&#xff0c;是一個…

密碼學數字信封的介紹

對稱密碼和非對稱密碼 對稱密碼&#xff1a;加解密運算非常快&#xff0c;適合處理大批量數據&#xff0c;但其密碼的分發與管理比較復雜非對稱密碼&#xff1a;公鑰和私鑰分離&#xff0c;非常適合密鑰的分發和管理 數字信封的定義 如果將對稱密碼算法和非對稱密碼算法的優點…

Android設計模式之——狀態模式

一、介紹 狀態模式中的行為是由狀態來決定的&#xff0c;不同的狀態下有不同的行為。狀態模式和策略模式的結構幾乎完全一樣&#xff0c;但它們的目的、本質卻完全不一樣。狀態模式的行為是平行的、不可替換的&#xff0c;策略模式的行為是彼此獨立、可相互替換的。用一句話來…

Android設計模式之——責任鏈模式

一、介紹 責任鏈模式&#xff08;Iterator Pattern&#xff09;&#xff0c;是行為型設計模式之一。什么是”鏈“&#xff1f;我們將多個節點首尾相連所構成的模型稱為鏈&#xff0c;比如生活中常見的鎖鏈&#xff0c;就是由一個個圓角長方形的鐵環串起來的結構。對于鏈式結構…

目前基于區塊鏈的檔案防篡改系統的設計如何實現防篡改

架構設計圖 分析 為了保障檔案數據的安全性和隱私性&#xff0c;存儲檔案附件和檔案屬性存儲加密存儲在私有IPFS集群&#xff0c;檔案的IPFS地址和數字指紋存儲在私有區塊鏈上。公有區塊鏈定期存儲和檢查私有區塊鏈最新不可逆區塊的高度和哈希值&#xff0c;以保障私有區塊鏈上…

IPFS的文件存儲模式

IPFS是如何進行文件存儲的 IPFS采用的索引結構是DHT&#xff08;分布式哈希表&#xff09;&#xff0c;數據結構是MerkleDAG&#xff08;Merkle有向無環圖&#xff09; DHT(分布式哈希表) 參考鏈接MerkleDAG&#xff08;Merkle有向無環圖&#xff09; 參考鏈接MerkleDAG功能…

Android設計模式之——解釋器模式

一、介紹 解釋器模式&#xff08;Interpreter Pattern&#xff09;是一種用的比較少的行為型模式&#xff0c;其提供了一種解釋語言的語法或表達式的方式&#xff0c;該模式定義了一個表達式接口&#xff0c;通過該接口解釋一個特定的上下文。在這么多的設計模式中&#xff0c…

在Docker里面安裝Ubuntu,并且使用ssh進行連接

創建Ubuntu鏡像 1&#xff0c;拉取Ubuntu系統的鏡像 docker pull ubuntu2、查看拉取是否成功 docker images3&#xff0c;運行容器 docker run --name 新建的容器的名字 -ti -v /AAA:/BBB -d -p 3316:22 ubuntu(這個是鏡像的名字)宿主機根目錄中的AAA文件夾就映射到了容器…

Android設計模式之——命令模式

一、介紹 命令模式&#xff08;Command Pattern&#xff09;&#xff0c;是行為型設計模式之一。命令模式相對于其他的設計模式來說并沒有那么多的條條框框&#xff0c;其實它不是一個很”規范“的模式&#xff0c;不過&#xff0c;就是基于這一點&#xff0c;命令模式相對于其…

C++ 序列化和反序列化學習

定義 程序員在編寫應用程序的時候往往需要將程序的某些數據存儲在內存中&#xff0c;然后將其寫入某個文件或是將它傳輸到網絡中的另一臺計算機上以實現通訊。這些過程將會涉及到程序數據轉化成能被存儲并傳輸的格式&#xff0c;因此被稱為“序列化”&#xff08;Serializatio…