Fly Weight 享元模式

動(dòng)機(jī)

有些程序需要大量帶有內(nèi)部共享狀態(tài)的對(duì)象實(shí)例。作為例子,我們?cè)O(shè)想一個(gè)戰(zhàn)爭(zhēng)游戲,里邊存在很多 soldier 對(duì)象,soldier 對(duì)象維持著士兵行為的圖形化表示,如移動(dòng)和射擊,另外還有士兵的生命值和在陣地中的位置。雖然創(chuàng)建大量的 soldier 對(duì)象是必要的,但這會(huì)消耗大量的內(nèi)存。

目的

此模式的目的是利用狀態(tài)共享來(lái)支撐需要大量實(shí)例對(duì)象的場(chǎng)景,這些對(duì)象有部分相同的內(nèi)部狀態(tài),另外一些則不一致。

實(shí)現(xiàn)

以下是享元模式的 UML 類圖 (Extrinsic State: 外部狀態(tài), Intrinsic State: 內(nèi)部狀態(tài) )


  • Flyweight 聲明一個(gè)接口,通過(guò)這個(gè)接口享元接收并根據(jù)外部狀態(tài)采取行動(dòng)
  • ConcreteFlyweight 實(shí)現(xiàn) Flyweight 接口并保存內(nèi)部狀態(tài)。ConcreteFlyweight 對(duì)象必須是可共享的。享元實(shí)例對(duì)象必須在保持內(nèi)部狀態(tài)的同時(shí)能夠?qū)ν獠繝顟B(tài)進(jìn)行操作。在戰(zhàn)爭(zhēng)游戲的例子里,圖形表示是內(nèi)部狀態(tài),位置和生命值是外部狀態(tài)。士兵移動(dòng)過(guò)程中,動(dòng)作行為操作位置這個(gè)外部狀態(tài)從而產(chǎn)生新的位置。
  • FlyweightFactory 此工廠新建和管理享元對(duì)象。此外,享元工廠確保享元對(duì)象的共享。工廠維持著一個(gè)包含各類享元的對(duì)象池。如果一類對(duì)象已經(jīng)創(chuàng)建過(guò)了,則直接從對(duì)象池中返回。如果是新的對(duì)象,則將其添加入對(duì)象池中。
    在戰(zhàn)爭(zhēng)游戲的例子中,士兵的享元工廠創(chuàng)建兩種類型的享元:士兵享元和上校享元。當(dāng)客戶端請(qǐng)求獲取一個(gè)士兵時(shí),享元工廠檢查對(duì)象池中是否已有士兵對(duì)象,有的話直接返回,沒(méi)有的話就新建一個(gè)士兵對(duì)象,塞入池中并返回給客戶端;下一次客戶端再請(qǐng)求士兵時(shí),將直接返回先前創(chuàng)建的士兵對(duì)象,不會(huì)再創(chuàng)建新的。
  • Client 客戶端維持享元對(duì)象的引用,此外還計(jì)算和維持外部狀態(tài)

如果客戶端需要一個(gè)享元對(duì)象,它會(huì)訪問(wèn)工廠來(lái)獲取。工廠檢查享元對(duì)象池,看請(qǐng)求的對(duì)象類型是否在池中,如果存在,則返回該對(duì)象的引用;如果不存在,工廠會(huì)創(chuàng)建該類對(duì)象,將其加入對(duì)象池中,并將其引用返回給客戶端。享元維持內(nèi)部狀態(tài)(我們創(chuàng)建享元來(lái)在大量對(duì)象中共享的狀態(tài))并提供方法來(lái)操作外部狀態(tài)(這些狀態(tài)在各個(gè)對(duì)象中各不相同)

適用范圍和例子

享元模式適用于適用存在大量對(duì)象的程序,并且這些對(duì)象的一部分內(nèi)部狀態(tài)可以共享,另外一部分狀態(tài)則有所不同。

示例 - 戰(zhàn)爭(zhēng)游戲

游戲?qū)嵗?個(gè) SoldierClient, 每個(gè) client 負(fù)責(zé)維護(hù)自己的內(nèi)部狀態(tài),這些狀態(tài)對(duì)于士兵享元來(lái)說(shuō)是外部的。盡管實(shí)例化了5個(gè) client, 但是僅使用了一個(gè) Soldier 享元對(duì)象。


源碼:design-patterns/flyweigh/Soldier

再一個(gè)例子 - 文本編輯器

面向?qū)ο蟮奈谋揪庉嬈餍枰獎(jiǎng)?chuàng)建字符對(duì)象 Character 來(lái)表示文件中的每個(gè)字符。每個(gè)字符對(duì)象 Character 維護(hù)著它是什么字符,是什么字體,字符的大小以及它在文件中的位置等信息。一個(gè)文件通常由及其龐大的字符對(duì)象集組成,也就會(huì)占用非常多的內(nèi)存。注意,一般來(lái)說(shuō)字符的數(shù)量(數(shù)字,字母和其他特殊字符)是已知并且固定的,而且能應(yīng)用于每個(gè)字符上的字體也是已知的;所以,通過(guò)創(chuàng)建管理字符類型信息(字母,數(shù)字等)和字體信息的 Letter 字母享元,然后再創(chuàng)建只維護(hù)字符在文件中位置的Letter Client 字母消費(fèi)者對(duì)象, 我們就大大降低了編輯器的內(nèi)存需求。
源碼: design-patterns/flyweigh/character

總結(jié)

享元模式通過(guò)在消費(fèi)端共享享元對(duì)象來(lái)節(jié)省內(nèi)存消耗。所能節(jié)省的內(nèi)存量取決于享元種類的個(gè)數(shù)(如上面例子中討論的士兵分類和上校分類)。

相關(guān)模式

工廠單例模式 - 享元模式一般會(huì)使用一個(gè)生產(chǎn)享元的工廠,并將單例應(yīng)用于這個(gè)工廠,這樣每個(gè)種類的享元只會(huì)返回一個(gè)實(shí)例。
狀態(tài)策略模式 - 狀態(tài)和策略模式的對(duì)象也經(jīng)常實(shí)現(xiàn)為享元模式

JDK中的應(yīng)用

java.lang.Integer#valueOf(int) (also on Boolean, Byte, Character, Short, Long and BigDecimal)
Long 的內(nèi)部類 LongCache 即為享元對(duì)象池,其中緩存了 [-128, 127] 范圍的長(zhǎng)整型包裝類實(shí)例; 如果通過(guò) Long.valueOf(n) 獲取實(shí)例,在范圍內(nèi)的就直接從 LongCache 中返回; Long 在其內(nèi)部保存基本類型 long (注意是小寫)的值,并提供 parseLong, intValue, floatValue, longValue 等方法返回基本類型值的外部狀態(tài),正是符合享元模式的使用場(chǎng)景。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1 場(chǎng)景問(wèn)題# 1.1 加入權(quán)限控制## 考慮這樣一個(gè)問(wèn)題,給系統(tǒng)加入權(quán)限控制,這基本上是所有的應(yīng)用系統(tǒng)都有的功能...
    七寸知架構(gòu)閱讀 2,586評(píng)論 1 57
  • 享元模式(Flyweight Pattern):運(yùn)用共享技術(shù)有效地支持大量細(xì)粒度對(duì)象的復(fù)用。系統(tǒng)只使用少量的對(duì)象,...
    breezedancer閱讀 1,960評(píng)論 2 51
  • 定義 Flyweight在拳擊比賽中指最輕量級(jí),即“蠅量級(jí)”或“雨量級(jí)”。這里選擇使用“享元模式”的意譯,是因?yàn)檫@...
    步積閱讀 1,967評(píng)論 0 2
  • 當(dāng)年看西游,看的是熱鬧;而今回頭看,所謂西游,前行的是人,更重要的是心。 唐僧西行若心猿意馬,再牛逼的悟...
    水清知沉耳閱讀 245評(píng)論 0 0
  • 總以為這輩子的酸甜苦辣 像是歸結(jié)在一張餅上的滋味 半邊甜美,半邊苦澀 小時(shí)候 拼命去啃噬那甜美的半張 像是活在蜜罐...
    聽(tīng)那冷雨閱讀 376評(píng)論 0 3

友情鏈接更多精彩內(nèi)容