設(shè)計(jì)模式-享元模式

設(shè)計(jì)模式-享元模式

定義

面向?qū)ο蠹夹g(shù)可以很好地解決一些靈活性或可擴(kuò)展性問題,但是很多情況下需要在系統(tǒng)中增加類和對(duì)象的個(gè)數(shù).當(dāng)對(duì)象數(shù)量太多時(shí),將導(dǎo)致運(yùn)行代價(jià)過高,帶來性能下降等問題.享元模式正是為解決這一類問題而誕生的.

享元模式(Flyweight Pattern)又稱為輕量級(jí)模式,是對(duì)象池的一種實(shí)現(xiàn).類似于線程池,線程池可以避免不停的創(chuàng)建和銷毀多個(gè)對(duì)象,消耗性能.提供了減少對(duì)象數(shù)量從而改善應(yīng)用所需的對(duì)象結(jié)構(gòu)的方式.其宗旨是共享細(xì)粒度對(duì)象,將多個(gè)對(duì)同一對(duì)象的訪問集中起來,不必為每個(gè)訪問者創(chuàng)建一個(gè)單獨(dú)的對(duì)象,從此來降低內(nèi)存的消耗,屬于結(jié)構(gòu)型模式.

享元模式把一個(gè)對(duì)象的狀態(tài)分為內(nèi)部狀態(tài)外部狀態(tài),內(nèi)部狀態(tài)即是不變的,外部狀態(tài)是變化的,然后通過共享不變的部分,達(dá)到減少對(duì)象數(shù)量并節(jié)約內(nèi)存的目的.

享元模式的本質(zhì)緩存共享對(duì)象,降低內(nèi)存消耗.

首先,我們來看向遠(yuǎn)模式的通用UML類圖:

從類圖上看,享元模式有三個(gè)參與角色.

抽象享元角色(FlyWeight):享元對(duì)象抽象基類或者接口,同時(shí)定義出對(duì)象的外部狀態(tài)和內(nèi)部狀態(tài)的接口或?qū)崿F(xiàn);

具體享元角色(ConcreteFlyWeight):實(shí)現(xiàn)抽象角色定義的業(yè)務(wù).該角色的內(nèi)部狀態(tài)處理應(yīng)該與環(huán)境無關(guān),不能出現(xiàn)同時(shí)改變內(nèi)部狀態(tài)和外部狀態(tài)的操作;

享元工廠(FlyWeightFactory):負(fù)責(zé)管理享元對(duì)象池和創(chuàng)建享元對(duì)象.

適用場(chǎng)景

當(dāng)系統(tǒng)中多處需要同一組信息時(shí),可以把這些信息封裝到一個(gè)對(duì)象中,然后對(duì)該對(duì)象進(jìn)行緩存,這樣,一個(gè)對(duì)象就可以提供給多處需要使用的地方,避免大量同一對(duì)象的多次創(chuàng)建,消耗大量內(nèi)存空間.

享元模式其實(shí)就是工廠模式的一個(gè)改進(jìn)機(jī)制,享元模式 同樣要求創(chuàng)建一個(gè)或一組對(duì)象,并且就是通過工廠方法生成對(duì)象的,只不過享元模式為工廠方法增加了緩存這一功能.

主要應(yīng)用場(chǎng)景:

1、常常應(yīng)用于系統(tǒng)底層的開發(fā),以便解決系統(tǒng)的性能問題.

2、系統(tǒng)有大量相似對(duì)象、需要緩沖池的場(chǎng)景.

在生活中的享元模式也很常見,比如各中介機(jī)構(gòu)的房源共享,再比如全國社保聯(lián)網(wǎng).

使用享元模式實(shí)現(xiàn)共享池業(yè)務(wù)

我們舉個(gè)例子,我們每年春節(jié)為了搶到一張回家的火車票都要大費(fèi)周折,進(jìn)而出現(xiàn)了很多刷票軟件,刷票軟件會(huì)將我們填寫的信息緩存起來,然后定時(shí)檢查余票信息.搶票的時(shí)候,我們肯定要查詢下有沒有我們需要的票,這里我們假設(shè)一張火車的信息包含:出發(fā)站蛤迎、目的站、價(jià)格谱煤、座位類別.現(xiàn)在要求編寫一個(gè)查詢火車票查詢偽代碼,可以通過出發(fā)站、目的站查到相關(guān)票.

比如:要求通過出發(fā)站、目的站查詢火車票,那么我們只需構(gòu)建出火車票類對(duì)象,然后提供一個(gè)查詢出發(fā)站庞钢、目的站的接口給到客戶進(jìn)行查詢即可,具體代碼如下:

創(chuàng)建ITicket接口
然后,創(chuàng)建TrainTicket類實(shí)現(xiàn)接口
最后創(chuàng)建票工廠
測(cè)試代碼
運(yùn)行結(jié)果

分析上面的代碼,我們發(fā)現(xiàn)客戶端進(jìn)行查詢時(shí),系統(tǒng)通過TicketFactory直接創(chuàng)建一個(gè)火車票對(duì)象,但是這樣做的話,當(dāng)某個(gè)瞬間如果有大量的用戶請(qǐng)求同一張票的信息時(shí),系統(tǒng)就會(huì)創(chuàng)建出大量該火車票對(duì)象,系統(tǒng)內(nèi)存壓力驟增.而其實(shí)更好的做法應(yīng)該是緩存該票對(duì)象,然后復(fù)用提供給其他查詢請(qǐng)求,這樣一個(gè)對(duì)象就足以支撐數(shù)以千計(jì)的查詢請(qǐng)求,對(duì)內(nèi)存完全無壓力,使用享元模式可以很好地解決這個(gè)問題.

我們繼續(xù)優(yōu)化代碼,只需要在TicketFactory類中進(jìn)行更改,增加緩存機(jī)制:

增加對(duì)象池
運(yùn)行結(jié)果

可以看到,除了第一次查詢創(chuàng)建對(duì)象后,后續(xù)查詢相同車次票信息都是使用緩存對(duì)象,無需創(chuàng)建新對(duì)象了.來看一下類結(jié)構(gòu)圖:

類圖

其中ITicket就是抽象享元角色,TrainTicket就是具體享元角色,TicketFactory就是享元工廠.有些小伙伴一定會(huì)有疑惑啦,這不就是注冊(cè)式的單例模式嗎?對(duì),這就是注冊(cè)式單例模式.孫然,結(jié)構(gòu)上很像,但是享元模式的重點(diǎn)在結(jié)構(gòu)上,而不是在創(chuàng)建對(duì)象上.后面看看享元模式在JDK源嗎匯總的一個(gè)應(yīng)用,大家應(yīng)該就能徹底明白了.

再比如:我們經(jīng)常使用的數(shù)據(jù)庫連接池,因?yàn)槲覀兪褂肅onnection對(duì)象時(shí)主要性能消耗在建立連接和關(guān)閉連接的時(shí)候,為了提高Connection在調(diào)用時(shí)的性能,我們將Connection對(duì)象在調(diào)用前創(chuàng)建好緩存起來,用的時(shí)候從緩存中取值,用完再放回去,達(dá)到資源重復(fù)利用的目的.來看下面代碼:

其實(shí)是維護(hù)了一個(gè)連接池

這樣的連接池,普遍應(yīng)用于開源框架,有效提升底層的運(yùn)行性能.

享元模式在源碼中的應(yīng)用

1、String中的享元模式

Java中將String定義為final(不可改變的),JVM中字符串一般保存在字符串常量池中,java會(huì)確保一個(gè)字符串在常量池中只有一個(gè)拷貝,這個(gè)字符串常量池在JDK6.0以前是位于常量池中,位于永久代,而在JDK7.0中,JVM將其從永久代拿出來放置于堆上.當(dāng)創(chuàng)建一個(gè)字符串時(shí),如果字符串常量池中有該對(duì)象對(duì)應(yīng)的字面量,則返回該字面量在字符串常量池中的引用,否則,創(chuàng)建復(fù)制一份該字面量到字符串常量池并返回它的引用.

2因谎、Integer中的享元模式

在Integer源碼中的valueOf()方法做了一個(gè)條件判斷,如果目標(biāo)值在-128到127之間,則直接從緩存中取值,否則新建對(duì)象.那JDK為何要這樣做呢?因?yàn)樵?128到127之間的數(shù)據(jù)在int范圍內(nèi)是使用最頻繁的,為了節(jié)省頻繁創(chuàng)建對(duì)象帶來的內(nèi)存消耗,這里就用到了享元模式,來提高性能.

3基括、Long中的享元模式

同上.

4、Apache Commons Pool2中的享元模式

一種對(duì)象池實(shí)現(xiàn).

享元模式的內(nèi)部狀態(tài)和外部狀態(tài)

享元模式的定義為我們提出了兩個(gè)要求:細(xì)粒度和共享對(duì)象.因?yàn)橐蠹?xì)粒度對(duì)象,所以不可避免地會(huì)使對(duì)象數(shù)量多且性質(zhì)相近,此時(shí)我們就將這些對(duì)象的信息分為兩個(gè)部分:內(nèi)部狀態(tài)和外部狀態(tài).

內(nèi)部狀態(tài)指對(duì)象共享出來的信息,存儲(chǔ)在享元對(duì)象內(nèi)部且不會(huì)隨環(huán)境的變化而變化;外部狀態(tài)指對(duì)象得以依賴的一個(gè)標(biāo)記,是隨環(huán)境變化而變化的财岔、不可共享的狀態(tài).

優(yōu)點(diǎn)

1风皿、減少對(duì)象的創(chuàng)建,降低內(nèi)存中對(duì)象的數(shù)量,降低系統(tǒng)的內(nèi)存,提高效率;

2、減少內(nèi)存之外的其他資源占用.

缺點(diǎn)

1匠璧、關(guān)注內(nèi)桐款、外狀態(tài),關(guān)注線程安全問題;

2、使系統(tǒng)夷恍、程序的邏輯復(fù)雜化.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末魔眨,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子裁厅,更是在濱河造成了極大的恐慌冰沙,老刑警劉巖侨艾,帶你破解...
    沈念sama閱讀 212,542評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件执虹,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡唠梨,警方通過查閱死者的電腦和手機(jī)袋励,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人茬故,你說我怎么就攤上這事盖灸。” “怎么了磺芭?”我有些...
    開封第一講書人閱讀 158,021評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵赁炎,是天一觀的道長。 經(jīng)常有香客問我钾腺,道長徙垫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,682評(píng)論 1 284
  • 正文 為了忘掉前任放棒,我火速辦了婚禮姻报,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘间螟。我一直安慰自己吴旋,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,792評(píng)論 6 386
  • 文/花漫 我一把揭開白布厢破。 她就那樣靜靜地躺著荣瑟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪摩泪。 梳的紋絲不亂的頭發(fā)上褂傀,一...
    開封第一講書人閱讀 49,985評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音加勤,去河邊找鬼仙辟。 笑死,一個(gè)胖子當(dāng)著我的面吹牛鳄梅,可吹牛的內(nèi)容都是我干的叠国。 我是一名探鬼主播,決...
    沈念sama閱讀 39,107評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼戴尸,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼粟焊!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起孙蒙,我...
    開封第一講書人閱讀 37,845評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤项棠,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后挎峦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體香追,經(jīng)...
    沈念sama閱讀 44,299評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,612評(píng)論 2 327
  • 正文 我和宋清朗相戀三年坦胶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了透典。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片晴楔。...
    茶點(diǎn)故事閱讀 38,747評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖峭咒,靈堂內(nèi)的尸體忽然破棺而出税弃,到底是詐尸還是另有隱情,我是刑警寧澤凑队,帶...
    沈念sama閱讀 34,441評(píng)論 4 333
  • 正文 年R本政府宣布则果,位于F島的核電站,受9級(jí)特大地震影響漩氨,放射性物質(zhì)發(fā)生泄漏短条。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,072評(píng)論 3 317
  • 文/蒙蒙 一才菠、第九天 我趴在偏房一處隱蔽的房頂上張望茸时。 院中可真熱鬧,春花似錦赋访、人聲如沸可都。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽渠牲。三九已至,卻和暖如春步悠,著一層夾襖步出監(jiān)牢的瞬間签杈,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評(píng)論 1 267
  • 我被黑心中介騙來泰國打工鼎兽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留答姥,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,545評(píng)論 2 362
  • 正文 我出身青樓谚咬,卻偏偏與公主長得像鹦付,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子择卦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,658評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容