5械拍、Android設(shè)計模式---(使程序運(yùn)行更高效)原型模式

一、介紹,定義

原型模式(Prototype Pattern)是用于創(chuàng)建重復(fù)的對象殊者,同時又能保證性能与境。

這種模式是實現(xiàn)了一個原型接口,該接口用于創(chuàng)建當(dāng)前對象的克隆猖吴。當(dāng)直接創(chuàng)建對象的代價比較大時摔刁,則采用這種模式。例如海蔽,一個對象需要在一個高代價的數(shù)據(jù)庫操作之后被創(chuàng)建共屈。我們可以緩存該對象,在下一個請求時返回它的克隆党窜,在需要的時候更新數(shù)據(jù)庫拗引,以此來減少數(shù)據(jù)庫調(diào)用。

二幌衣、使用場景

1類初始化需要消耗非常多的資源矾削,包括數(shù)據(jù)庫、硬件資源等通過原型復(fù)制避免這些消耗豁护。
2通過new產(chǎn)生一個對象需要非常繁瑣的數(shù)據(jù)準(zhǔn)備或訪問權(quán)限,這時可以使用原型模式;
3一個對象需要提供給其他對象訪問,而且各個調(diào)用者可能都需要修改其值時,可以考慮使用原型模式拷貝多個對象共調(diào)用者使用,即保護(hù)性拷貝.
通過實現(xiàn)Cloneable接口的原型模式在調(diào)用clone函數(shù)構(gòu)造實例時并不一定比new快哼凯,只有new的成本較高、耗時時楚里,通過clone方法才能獲得效率上的提升断部。

注:Android系統(tǒng)中很多地方都用到了原型模式,除此之外班缎,像比較有名的OkHttp蝴光、realm數(shù)據(jù)庫…都用到了原型模式

三、原型模式UML類圖

4.png

Client:客戶端
Prototype:抽象類或者接口达址,聲明具備clone能力
Concreteprototype:具體的原型類

四蔑祟、原型模式簡單實現(xiàn)

例子中首先創(chuàng)建了一個文檔對象,即WordDocument苏携,這個文檔中含有文字和圖片做瞪。用戶經(jīng)過了長時間的內(nèi)容編輯后,打算對該文檔做進(jìn)一步的編輯右冻,但是装蓬,這個編輯后的文檔是否會被采用還不確定,因此纱扭,為了安全起見牍帚,用戶需要將當(dāng)前文檔拷貝一份,然后再在文檔副本上進(jìn)行修改乳蛾,這與《Effective Java》一書中提到的保護(hù)性拷貝有些類似暗赶,如此鄙币,這個原始文檔就是我們上述所說的樣板實例,也就是將要被“克隆”的對象蹂随,我們稱為原型:

/**
 * 文檔類型十嘿,扮演的是ConcretePrototype角色,而cloneable是代表prototype角色
 */
public class WordDocument implements Cloneable {
 //文本
 private String mText;
 //圖片名列表
 private ArrayList<String> mImages = new ArrayList<String>();
 public WordDocument(){
  System.out.println("-------- WordDocument構(gòu)造函數(shù) --------");
 }
 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 --------");
 }
 @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 static void main(String[] args) throws IOException {
  //1.構(gòu)建文檔對象
  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();
}

結(jié)果

-------- WordDocument構(gòu)造函數(shù) --------
//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 --------

這里我們發(fā)現(xiàn)通過修改doc2后,只是影響了originDoc的mImages激率,而沒有改變mText咳燕。

五、重點

clone拷貝對象并不會執(zhí)行構(gòu)造函數(shù)乒躺,當(dāng)有一些特殊初始化時招盲,需要注意;

上述原型模式的實現(xiàn)實際上只是一個淺拷貝嘉冒,也稱影子拷貝曹货,這份拷貝實際上并不是將原始的文檔的所有字段都重新構(gòu)造了一份,而是副本文檔的字段引用原始文檔的字段讳推,如下圖:

5.png

細(xì)心的讀者可能從上面的結(jié)果中發(fā)現(xiàn)控乾,最后兩個文檔信息輸出是一致的。我們在doc2添加了一張圖片娜遵,但是,同時也顯示在originDoc中壤短,這是怎么回事呢设拟?學(xué)習(xí)過C++的讀者都會有比較深刻的體會,這是因為上文中WordDocument的clone方法中只是簡單的進(jìn)行了淺拷貝久脯,引用類型的新對象doc2.mImages只是單純的指向了this.mImages引用纳胧,并沒有重新構(gòu)造一個mImages對象,然后將原始文檔中的圖片添加到新的mImages對象中帘撰,這樣就導(dǎo)致doc2.mImages與原始文檔中的是同一個對象跑慕,因此,修改了其中一個文檔中的圖片摧找,另一個文檔也會受影響核行。那么如何解決這個問題呢?答案就是采用深拷貝蹬耘,即在拷貝對象時芝雪,對于引用型的字段也要采用拷貝的形式,而不是單純引用的形式综苔。

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

@Override
protected WordDocument clone(){
  try{
   WordDocument doc = (WordDocument)super.clone();
   doc.mText = this.mText;
   //對mImages對象也調(diào)用clone()函數(shù)惩系,進(jìn)行深拷貝
   doc.mImages = (ArrayList<String>)this.mImages.clone();
   return doc;
  }catch(Exception e){}
  return null;
}

修改后在執(zhí)行上述代碼的結(jié)果是:

-------- WordDocument構(gòu)造函數(shù) --------
//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 --------

可以看出現(xiàn)在互不影響位岔,這個叫做深拷貝。

接著上面的疑問堡牡,其實String類型在淺拷貝時和引用類型一樣抒抬,沒有單獨復(fù)制,而是引用同一地址晤柄,因為String沒有實現(xiàn)cloneable接口擦剑,也就是說只能復(fù)制引用。(這里我們可以查看源碼可以看到可免,而ArrayList實現(xiàn)了cloneable接口)但是當(dāng)修改其中的一個值的時候抓于,會新分配一塊內(nèi)存用來保存新的值,這個引用指向新的內(nèi)存空間浇借,原來的String因為還存在指向他的引用捉撮,所以不會被回收,這樣妇垢,雖然是復(fù)制的引用巾遭,但是修改值的時候,并沒有改變被復(fù)制對象的值闯估。

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

原型模式是非常簡單的一個模式骑素,它的核心問題就是對原始對象進(jìn)行拷貝,在這個模式的使用過程中需要注意的一點就是:深刚夺、淺拷貝的問題献丑。在開發(fā)過程中,為了減少錯誤侠姑,作者建議使用該模式時盡量使用深拷貝创橄,避免操作副本時影響原始對象的問題。

六莽红、總結(jié)

原型模式本質(zhì)上就是對象的拷貝妥畏,與C++中的拷貝構(gòu)造函數(shù)有些類似,它們之間容易出現(xiàn)的問題也都是深拷貝安吁、淺拷貝醉蚁。使用原型模式可以解決構(gòu)建復(fù)雜對象的資源消耗問題,能夠在某些場景下提升創(chuàng)建對象的效率柳畔。
優(yōu)點:
(1)原型模式是在內(nèi)存中二進(jìn)制流的拷貝馍管,要比直接new一個對象性能好很多,特別是要在一個循環(huán)體內(nèi)產(chǎn)生大量對象時薪韩,原型模式可能更好的體現(xiàn)其優(yōu)點确沸。
(2)還有一個重要的用途就是保護(hù)性拷貝捌锭,也就是對某個對象對外可能是只讀的,為了防止外部對這個只讀對象的修改罗捎,通彻矍可以通過返回一個對象拷貝的形式實現(xiàn)只讀的限制。
缺點:
(1)這既是它的優(yōu)點也是缺點桨菜,直接在內(nèi)存中拷貝豁状,構(gòu)造函數(shù)是不會執(zhí)行的,在實際開發(fā)中應(yīng)該注意這個潛在問題倒得。優(yōu)點是減少了約束泻红,缺點也是減少了約束,需要大家在實際應(yīng)用時考慮霞掺。
(2)通過實現(xiàn)Cloneable接口的原型模式在調(diào)用clone函數(shù)構(gòu)造實例時并不一定比通過new操作速度快谊路,只有當(dāng)通過new構(gòu)造對象較為耗時或者說成本較高時,通過clone方法才能夠獲得效率上的提升菩彬。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末缠劝,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子骗灶,更是在濱河造成了極大的恐慌惨恭,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件耙旦,死亡現(xiàn)場離奇詭異脱羡,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)免都,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進(jìn)店門轻黑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人琴昆,你說我怎么就攤上這事」萑啵” “怎么了业舍?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長升酣。 經(jīng)常有香客問我舷暮,道長,這世上最難降的妖魔是什么噩茄? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任下面,我火速辦了婚禮,結(jié)果婚禮上绩聘,老公的妹妹穿的比我還像新娘沥割。我一直安慰自己耗啦,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布机杜。 她就那樣靜靜地躺著帜讲,像睡著了一般。 火紅的嫁衣襯著肌膚如雪椒拗。 梳的紋絲不亂的頭發(fā)上似将,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天,我揣著相機(jī)與錄音蚀苛,去河邊找鬼在验。 笑死,一個胖子當(dāng)著我的面吹牛堵未,可吹牛的內(nèi)容都是我干的腋舌。 我是一名探鬼主播,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼兴溜,長吁一口氣:“原來是場噩夢啊……” “哼侦厚!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起拙徽,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤刨沦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后膘怕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體想诅,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年岛心,在試婚紗的時候發(fā)現(xiàn)自己被綠了来破。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡忘古,死狀恐怖徘禁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情髓堪,我是刑警寧澤送朱,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站干旁,受9級特大地震影響驶沼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜争群,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一回怜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧换薄,春花似錦玉雾、人聲如沸翔试。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽遏餐。三九已至,卻和暖如春赢底,著一層夾襖步出監(jiān)牢的瞬間失都,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工幸冻, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留粹庞,地道東北人。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓洽损,卻偏偏與公主長得像庞溜,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子碑定,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,047評論 2 355