設計模式(四):原型模式

什么是原型模式翘骂?為什么要使用原型模式?

前兩天面試了一個95年碩士畢業(yè)的小姐姐,在杭州某大廠工作了兩年葫松,最近想回家鄉(xiāng)發(fā)展

對于兩年以上工作經驗的候選人,我都會問一些和設計模式相關的面試題

不得不面對一個現(xiàn)實底洗,大部分候選人對設計模式都沒有很深入的理解腋么,回答的并不出彩

當我對這個小姐姐提出這兩個問題時,也沒抱有很高的期望亥揖。沒想到小姐姐的回答很讓人意外珊擂,甚至可以說是讓我對原型模式有了更深刻的理解

為什么要使用原型模式

假如有一個類,命名為A费变。A類里面有兩個屬性摧扇,分別是x和y,并為這兩個屬性提供對應的get和set方法

將這個類的實體對象a作為test方法的參數(shù)

要求在test方法內利用a對象的某些屬性進行一些業(yè)務邏輯處理挚歧,但不能改變a對象的原有屬性

我們進行第一次嘗試:聲明一個新的對象a1扛稽,并把a賦值給它。讓test方法利用a1對象的屬性進行業(yè)務邏輯處理

publicstaticvoidtest(A?a){

A?a1?=?a;

System.out.println("test方法開始業(yè)務邏輯處理");

a1.setX(1);

}

我們來驗證一下是否會影響到a對象的屬性

publicstaticvoidmain(String[]?args){

A?a?=newA();

a.setX(0);

System.out.println("調用test方法前x="+?a.getX());

test(a);

System.out.println("調用test方法后x="+?a.getX());

}

輸出結果為

從輸出結果來看滑负,test方法改變了a對象的屬性在张,不符合要求。所以矮慕,第一次嘗試失敗

其實也不難理解帮匾,我們都知道JVM加載對象后會給對象分配內存空間

加載完a之后,給a分配一個空間

在加載a1的時候痴鳄,因為a1是將a的值賦值給了a1瘟斜,所以在給a1分配空間時,只是把a1的引用指向了a所在的內存地址,并沒有給a1分配獨立的內存空間

所以修改a1對象的屬性時哼转,a對象也會被改變

我們調整思路進行第二次嘗試:重新new一個新對象a2明未,把a對象的所有屬性值賦值給a2。test方法利用a2對象進行業(yè)務邏輯處理

publicstaticvoidtest(A?a){

A?a2?=newA();

a2.setX(a.getX());

a2.setY(a.getY());

System.out.println("test方法開始業(yè)務邏輯處理");

a2.setX(1);

a2.setY(2);

}

同樣來驗證一下是否會影響到a對象的屬性

publicstaticvoidmain(String[]?args){

A?a?=newA();

a.setX(0);

a.setY(0);

System.out.println("調用test方法前x="+?a.getX()?+"壹蔓,y="+?a.getY());

test(a);

System.out.println("調用test方法后x="+?a.getX()?+"趟妥,y="+?a.getY());

}

輸出結果為

這次的輸出結果顯示,test方法并沒有改變a對象的屬性佣蓉,符合要求

但是披摄,有一個問題

如果a不是一個具體的實例,而是一個抽象類或者接口勇凭。抽象類或者接口是不能被new的疚膊,該怎么辦?

這時候就要使用到原型模式來解決我們的問題了

原型模式

原型模式定義

「原型模式」可以讓你復制或克隆一個已有對象虾标,而又無需使你的代碼依賴這個對象所屬的類

通過定義我們可以提取出來兩個關鍵信息

第一寓盗,原型模式主要作用是復制或克隆一個已有對象

第二,去復制這個對象時不需要依賴這個對象所屬的類

這句話很有意思璧函,想要創(chuàng)建一個對象但是不用依賴這個對象所屬的類傀蚌,這要怎么實現(xiàn)?

答案就是把創(chuàng)建對象的過程交給這個類來處理

原型模式實戰(zhàn)

我們用原型模式來優(yōu)化一下上面的例子

動手之前我們需要知道原型模式的設計思路

根據(jù)定義可以知道在原型模式中蘸吓,對象的創(chuàng)建過程是交給對象所屬的類來處理的善炫,所以這個類肯定要提供一個方法,方法的返回值是這個對象库继。通常這個方法叫clone()或copy()

套用到上面例子的A類中箩艺,需要在A類里面提供一個clone()方法,在方法中創(chuàng)建一個當前對象并返回

在test方法中利用clone()來獲取一個a3對象去執(zhí)行業(yè)務邏輯

publicstaticvoidtest(A?a){

A?a3?=?a.clone();

System.out.println("test方法開始業(yè)務邏輯處理");

a3.setX(1);

}

再驗證一下是否改變了a對象的屬性

從輸出結果可以看到是沒有改變a對象的屬性的

那我們再來解決上面例子中遇到的問題宪萄,假如A是一個抽象類艺谆,該怎么去創(chuàng)建這個對象

其實也很簡單,抽象類中是可以有抽象方法的拜英。把clone()方法定義為抽象方法静汤,讓子類去實現(xiàn)它

假如A有兩個子類,分別是SubA1和SubA2聊记。兩個子類分別繼承A抽象類,并實現(xiàn)clone()抽象方法

在test中還是使用a.clone()就可以得到一個新的對象恢暖,而且不會影響到原有的a對象

這就用原型模式對上面的例子完成了優(yōu)化

深拷貝排监、淺拷貝

在java中,默認Object類是所有類的父類杰捂,在Object中有一個clone()方法

它是java默認提供的用來復制對象的方法舆床,這個方法是native修飾的,說明它是對操作系統(tǒng)的底層直接調用的,在理論上挨队,用它來復制對象性能會更好

所以谷暮,我們可以使用java.lang.Object#clone()來實現(xiàn)原型模式,總共分為兩步

被復制的類需要實現(xiàn)Cloneable這個接口類盛垦。這個接口類里面是沒有任何一個方法的湿弦,只是起到一個標記作用,也可以理解成一種約定(「約定大于配置」)

被復制的類需要重寫Object中的clone()方法

下面我們就來優(yōu)化一下A類

這樣寫出的原型模式腾夯,在理論上執(zhí)行效率更高颊埃。看似完美蝶俱,實則不然

假如A類里面有一個ArrayList屬性

我們來看一下班利,在clone完a后得到a4,改變a4的list屬性榨呆,會不會對a造成影響

輸出結果為

在修改a4對象時罗标,也改變了a對象的屬性值,這不是我們期望的結果

這是因為:Object在clone時只會對基礎類型的數(shù)據(jù)進行拷貝积蜻,引用類型的數(shù)據(jù)并沒有真正的拷貝闯割,而是把引用指針指向了這個數(shù)據(jù)在內存中的地址(還記得上文中a和a1指向同一個內存地址的例子嗎

這種只拷貝基礎數(shù)據(jù)類型的行為,我們稱之為淺拷貝浅侨。既可以拷貝基礎數(shù)據(jù)類型纽谒,又可以拷貝引用數(shù)據(jù)類型的行為,我們稱之為深拷貝

在原型模式中如输,我們應該使用深拷貝來復制對象鼓黔。

要實現(xiàn)深拷貝,「需要這個引用類型的數(shù)據(jù)所屬的類也實現(xiàn)Cloneable接口不见,并且重寫Object類的clone()方法」

在本例子中澳化,引用類型所屬的類是ArrayList,它本身已經實現(xiàn)了Cloneable接口稳吮,并重寫了Object類的clone()方法缎谷。

我們只需要在A類的clone()方法中調用ArrayList的clone()方法即可

這樣就基于深拷貝完成了原型模式

總結

「原型模式」也叫「克隆模式」,它屬于設計模式三大類型中的創(chuàng)建型模式

在你需要復制一個對象灶似,而又不希望改變原有對象的時候可以考慮使用原型模式來實現(xiàn)

在實現(xiàn)原型模式時列林,引用類型數(shù)據(jù)的復制要基于深拷貝,否則會影響到被拷貝的原型

在Spring生態(tài)下酪惭,對象的創(chuàng)建基本都由IOC來實現(xiàn)希痴,原型模式好像沒有多少用武之地

但是,用的少不代表沒用春感。我們在學習設計模式時砌创,目的不僅僅在于要學會設計模式虏缸,而是要學會設計模式使用的設計思想

學會這種思想,沉淀為自己的思路嫩实,在工作中能實現(xiàn)舉一反三刽辙,才能無往不利

-- 以上內容來自公眾號「赫連小伍」,轉載請注明出處

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末甲献,一起剝皮案震驚了整個濱河市宰缤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌竟纳,老刑警劉巖撵溃,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異锥累,居然都是意外死亡缘挑,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門桶略,熙熙樓的掌柜王于貴愁眉苦臉地迎上來语淘,“玉大人,你說我怎么就攤上這事际歼』谭” “怎么了?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵鹅心,是天一觀的道長吕粗。 經常有香客問我,道長旭愧,這世上最難降的妖魔是什么颅筋? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮输枯,結果婚禮上议泵,老公的妹妹穿的比我還像新娘。我一直安慰自己桃熄,他們只是感情好先口,可當我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著瞳收,像睡著了一般碉京。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上螟深,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天谐宙,我揣著相機與錄音,去河邊找鬼血崭。 笑死卧惜,一個胖子當著我的面吹牛,可吹牛的內容都是我干的夹纫。 我是一名探鬼主播咽瓷,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼舰讹!你這毒婦竟也來了茅姜?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤月匣,失蹤者是張志新(化名)和其女友劉穎钻洒,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體锄开,經...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡素标,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了萍悴。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片头遭。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖癣诱,靈堂內的尸體忽然破棺而出计维,到底是詐尸還是另有隱情,我是刑警寧澤撕予,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布鲫惶,位于F島的核電站,受9級特大地震影響实抡,放射性物質發(fā)生泄漏欠母。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一澜术、第九天 我趴在偏房一處隱蔽的房頂上張望艺蝴。 院中可真熱鬧,春花似錦鸟废、人聲如沸猜敢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缩擂。三九已至,卻和暖如春添寺,著一層夾襖步出監(jiān)牢的瞬間胯盯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工计露, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留博脑,地道東北人憎乙。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像叉趣,于是被迫代替她去往敵國和親泞边。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內容

  • 個人學習筆記分享疗杉,當前能力有限阵谚,請勿貶低,菜鳥互學烟具,大佬繞道如有勘誤梢什,歡迎指出和討論,本文后期也會進行修正和補充 ...
    Echo_YeZ閱讀 201評論 0 0
  • 前言本文是對《Adroid 源碼設計模式解析與實戰(zhàn)》 何紅輝朝聋、關愛民 著 人民郵電出版社所做的讀書筆記嗡午。文章是對本...
    肖丹晨閱讀 579評論 0 2
  • 前言 前面我們學習了日常開發(fā)中使用頻率最高的 單例模式 今天就讓我們一起來學習下另外一個很簡單的模式:原型模式 其...
    X_Meteor閱讀 1,360評論 0 4
  • 一、概述 原型模式(Prototype Pattern)用于創(chuàng)建重復的對象冀痕,同時又能保證性能翼馆。它屬于創(chuàng)建型設計模式...
    12313凱皇閱讀 3,877評論 1 1
  • 原型模式 Prototype Pattern 它是創(chuàng)建型模式中的一種,提供了一種創(chuàng)建對象的最佳方式金度,用于創(chuàng)建重復的...
    Lost_Robot閱讀 235評論 0 0