23種設計模式-結構型模式-享元

文章目錄

  • 簡介
  • 問題
  • 解決方案
    • 享元與不可變性
    • 享元工廠
  • 代碼
  • 總結

簡介

亦稱:緩存、Cache、Flyweight。享元是一種結構型設計模式,它摒棄了在每個對象中保存所有數據的方式,通過共享多個對象所共有的相同狀態,讓你能在有限的內存容量中載入更多對象。

問題

假如你開發了一款簡單的游戲:玩家可以在地圖上移動并相互射擊,比如刺激戰場。你決定實現一個真實的粒子系統。大量的子彈、導彈和爆炸彈片會在整個地圖上穿行,給玩家提供刺激的游戲體驗。
開發完成后,你編譯好打包然后發送給了一個朋友進行測試。盡管這個游戲在你的電腦上正常運行,但是你的朋友卻無法長時間進行游戲:游戲總是會在他的電腦上運行幾分鐘后崩潰。在研究了調試信息后,你發現導致游戲崩潰的原因是電腦內存容量不足。朋友的設備性能遠比不上你的電腦,所以游戲運行之后很快就會出現問題。

真正的問題其實是跟粒子系統有關。每個粒子(一顆子彈、一枚導彈或一塊彈片)都由包含完整數據的獨立對象來表示,如下圖,每個粒子占用約21KB內存。當玩家打到高潮的時候(粒子數大約有1000000),會占用大約21GB的內存,這時內存已經不能再容納新粒子了,于是程序就崩潰了。

在這里插入圖片描述

解決方案

仔細觀察粒子Par-ti-cle類,你可能會注意到顏色(color)和紋理圖(sprite)這兩個成員變量所消耗的內存要比其他變量多得多。而且對于所有的粒子來說,這兩個成員變量所存儲的數據幾乎完全一樣(比如所有子彈的顏色和紋理圖都一樣),如下圖(Particle)。
在這里插入圖片描述
每個粒子的另一些狀態(坐標、移動矢量和速度)則是不同的(MovingParticle)。因為這些成員變量的數值會不斷變化。這些數據能夠代表粒子在創建之后不斷變化的情景,但每個粒子的顏色和紋理圖其實會保持不變。
我們知道,對象的常量數據通常被稱為內在狀態,它在對象里面,其他對象只能讀取但不能修改他的數值。而對象的其他狀態常常能被其他對象“從外部”改變,因此被稱為外在狀態。
享元模式建議不在對象中存儲外在狀態,而是把他傳遞給依賴于它的一個特殊方法。程序只在對象里保存內在狀態,以方便在不同情景下重用。這些對象的區別僅在于他的內在狀態(和外在狀態相比,內在狀態的變化要少很多),因此你所需的對象數量會大大削減。
讓我們回到游戲的示例。假如能從粒子類里抽出外在狀態(MovingParticle),那么我們只需要三個不同的對象(子彈、導彈和彈片)就能表示游戲中的所有粒子。如下圖,我們把MovingParticleParticle中抽出來之后,只要一個Particle對象存儲顏色和紋理圖(21KB)即可, 其余外在狀態都存儲在MovingParticle對象中(32B),MovingParticle 對象會共享這一個Particle對象。你現在很可能已經猜到了,我們把這樣一個僅存儲內在狀態(Particle)的對象稱為享元
在這里插入圖片描述

享元與不可變性

由于享元對象可以在不同的情景中使用,你必須確保他的狀態不能被修改。享元類的狀態只能由構造函數的參數進行一次性初始化,它不能對其他對象公開他的setter或公有成員變量。

享元工廠

為了能更方便地訪問各種享元,你可以創建一個工廠方法來管理已有享元對象的緩存池。工廠方法從客戶端處接收目標享元對象的內在狀態作為參數,如果它能在緩存池中找到所需享元,就把它返回給客戶端;如果沒有找到,它就會新建一個享元,并把他添加到緩存池里。
你可以選擇在程序的不同地方放入這個函數。最簡單的選擇就是把他放在享元容器里。除此之外,你還可以新建一個工廠類,或者創建一個靜態的工廠方法并把它放在實際的享元類里。

代碼

// 粒子內在狀態
final class ParticleType {private final String color;  // 不可變特征private final String texture;private final String effectType;public ParticleType(String color, String texture, String effectType) {this.color = color;this.texture = texture;this.effectType = effectType;}public void render(String position, double velocity) {System.out.printf("繪制%s特效: 位置%s | 速度%.1fm/s | 材質[%s]%n",effectType, position, velocity, texture);}
}// 粒子外在狀態載體
class Particle {private double x, y;private double velocity;private final ParticleType type; // 共享引用public Particle(double x, double y, double v, ParticleType type) {this.x = x;this.y = y;this.velocity = v;this.type = type;}public void display() {type.render(String.format("(%.1f, %.1f)", x, y), velocity);}
}// 享元工廠
class ParticleFactory {private static final Map<String, ParticleType> pool = new HashMap<>();public static ParticleType getType(String color, String texture, String effect) {String key = color + "_" + texture + "_" + effect;// 池化檢測邏輯if (!pool.containsKey(key)) {pool.put(key, new ParticleType(color, texture, effect));}return pool.get(key);}
}// 粒子系統管理
class ParticleSystem {private List<Particle> particles = new ArrayList<>();public void addParticle(double x, double y, double v,String color, String texture, String effect) {ParticleType type = ParticleFactory.getType(color, texture, effect);particles.add(new Particle(x, y, v, type));}public void simulate() {particles.forEach(Particle::display);}
}// 運行示例
class GameEngine {public static void main(String[] args) {ParticleSystem system = new ParticleSystem();// 添加火焰粒子system.addParticle(10.5, 20.3, 5.2, "橙紅", "fire_tex", "火焰");system.addParticle(15.1, 18.7, 4.8, "橙紅", "fire_tex", "火焰");// 添加冰雪粒子system.addParticle(30.0, 50.0, 2.1, "冰藍", "snow_tex", "冰霜");system.simulate();}
}

總結

在這里插入圖片描述

  1. 享元模式只是一種優化。在應用這個模式之前,你要確定程序里存在內存消耗問題,并且這個問題是跟大量類似對象同時占用內存相關的,同時確保這個問題沒辦法用其他更好的方式來解決。
  2. 享元(Fly-weight)類包含原始對象里部分能在多個對象中共享的狀態。同一享元對象可以在許多不同情景中使用。享元中存儲的狀態被稱為“內在狀態”。
  3. 情景(Con-text)類包含原始對象里各不相同的外在狀態情景和享元對象組合在一起就能表示原始對象的全部狀態
  4. 通常情況下,原始對象的行為會保留在享元類中。因此調用享元的方法必須提供部分外在狀態作為參數。但你也可把行為移動到情景類里,然后把連入的享元作為單純的數據對象。
  5. 客戶端(Client)負責計算或存儲享元的外在狀態。在客戶端看來,享元是一種可以在運行時進行配置的模板對象,具體的配置方式就是向他的方法里面傳入一些情景數據參數。
  6. 享元工廠(Fly-weight Fac-to-ry)會對已有享元的緩存池進行管理。有了工廠后,客戶端就無需直接創建享元,它們只需調用工廠并且向他傳遞目標享元的一些內在狀態就可以了。工廠會根據參數在之前已創建的享元中進行查找,如果找到滿足條件的享元就直接返回;如果沒有找到就根據參數新建享元。

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

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

相關文章

MFC BCGControlBar

BCGControlBar&#xff08;也稱為 BCGSoft 或 BCGControlBar Library&#xff09;是一個用于 MFC&#xff08;Microsoft Foundation Classes&#xff09; 的擴展庫&#xff0c;主要提供現代化的 UI 控件、Ribbon 界面、工具欄、屬性網格等組件&#xff0c;幫助開發者快速構建專…

【算法手記9】OR26 最長回文子串 NC369 [NOIP2002 普及組] 過河卒

&#x1f984;個人主頁:修修修也 &#x1f38f;所屬專欄:刷題 ??操作環境:牛客網 一.OR26 最長回文子串 牛客網題目鏈接(點擊即可跳轉):OR26 最長回文子串 題目詳情: 本題詳情如下圖: 題目思路: 本題解題思路如下: 本題思路用中心擴展算法,遍歷所有字符,將每個字符作為回文串…

批量刪除或替換文本文件中指定的行,如刪除第一行、刪除最后一行

每一個文本文件中我們都可以插入非常多的行&#xff0c;我們可以對行的內容進行刪除、修改等各種操作。如果文本文件中的某些行的內容需要更新&#xff0c;那我們就需要對其進行修改操作。想要修改文本文件的內容其實是非常方便的&#xff0c;但是如果想要批量的對多個文本文件…

LLM架構解析:詞嵌入模型 Word Embeddings(第二部分)—— 從基礎原理到實踐應用的深度探索

本專欄深入探究從循環神經網絡&#xff08;RNN&#xff09;到Transformer等自然語言處理&#xff08;NLP&#xff09;模型的架構&#xff0c;以及基于這些模型構建的應用程序。 本系列文章內容&#xff1a; NLP自然語言處理基礎詞嵌入&#xff08;Word Embeddings&#xff09…

機構數據服務

一、背景說明 券商/基金/銀行等金融機構的數據中心&#xff0c;基本都外購有數十家各類數據&#xff0c;自有業務每天也在產生海量信息。如何有效管理和使用這些數據&#xff0c;通過數據服務&#xff0c;沉淀數據資產&#xff0c;機構研發和運維部門也在不斷嘗試和改進。 傳…

中和農信:讓金融“活水”精準澆灌鄉村沃土

2025年政府工作報告首提“投資于人”概念&#xff0c;并22次提及“金融”&#xff0c;強調要著力抓好“三農”工作&#xff0c;深入推進鄉村全面振興&#xff1b;一體推進地方中小金融機構風險處置和轉型發展&#xff1b;扎扎實實落實促進民營經濟發展的政策措施&#xff0c;切…

JavaScript重難點突破:期約與異步函數

同步和異步 ?同步&#xff08;Synchronous&#xff09;? ?定義&#xff1a;任務按順序依次執行&#xff0c;前一個任務完成前&#xff0c;后續任務必須等待。 ?特點&#xff1a;阻塞性執行&#xff0c;程序邏輯直觀&#xff0c;但效率較低 ?異步&#xff08;Asynchron…

學習總結 網格劃分+瞬態求解設置

網格劃分部分 1.導入幾何文件 導入我們的幾何模型&#xff0c;他的格式為.scdocx 2.添加局部尺寸BOI 因為要對對前緣和尾緣進行局部加密&#xff0c;所以進行一個BOI的局部加密&#xff0c;目標尺寸取的幾何尺寸的最小尺寸的0.1&#xff0c;就是0.4mm。 3.生成表面網格 表面…

.NET 使用 WMQ 連接Queue 發送 message 實例

1. 首先得下載客戶端&#xff0c;沒有客戶端無法發送message. 安裝好之后長這樣 我裝的是7.5 安裝目錄如下 tools/dotnet 目錄中有演示的demo 2. .Net 連接MQ必須引用bin目錄中的 amqmdnet.dll 因為他是創建Queuemanager 的核心庫&#xff0c; 項目中引用using IBM.WMQ; 才…

風電行業預測性維護解決方案:給風機裝上 “智能醫生”,實現故障 “秒級預警”

引言&#xff1a;風電設備故障為何成為 “運維黑洞”&#xff1f; 某海上風電場因齒輪箱軸承故障停機 3 天&#xff0c;直接損失 50 萬元發電量。傳統維護模式下&#xff0c;人工巡檢覆蓋率不足 40%&#xff0c;故障修復平均耗時 72 小時。而預測性維護通過物聯網 AI 技術&am…

5、無線通信基站的FPGA實現架構

基站&#xff08;Base Station&#xff0c;BS&#xff09;&#xff0c;也稱為公用移動通信基站&#xff0c;是無線電臺站的一種形式&#xff0c;具體則指在一定的無線電覆蓋區中&#xff0c;通過移動通信交換中心&#xff0c;與移動電話終端之間的信息傳遞的無線電收發信電臺。…

筆記2——網絡參考模型

一、OSI參考模型&#xff1a; 應用層&#xff1a; 報文 給應用程序提供接口 表示層&#xff1a; 進行數據格式的轉換 會話層&#xff1a; 在通訊雙方之間建立、管理和終止會話 傳輸層&#xff1a; 數據段&#xff1b;建立、維護、取消一次端到端的數據傳輸過程&#xff1b;控制…

最短路徑:Bellman-Ford算法

Bellman-Ford的操作步驟 1.初始化距離&#xff1a;將起點的dist值設置為0&#xff0c;其他點的dist值設置為無窮大。 2.執行n-1輪松弛操作&#xff1a;遍歷所有邊&#xff0c;更新最短距離&#xff0c;收斂后可獲得最短路徑。 3.檢測負權環&#xff1a;額外遍歷一次&#xf…

0402-對象和類(訪問器 更改器 日期類)

OOP&#xff1a;面向對象程序設計 類&#xff1a;構造對象的模板或藍圖 類構造對象的過程稱為創建類的實例 封裝&#xff1a;對外隱藏數據的真實實現方式&#xff0c;提供簡單的方法 &#xff08;類比方向盤&#xff09; 對象&#xff1a;本質上是內存中的一小塊空間 識別類&a…

【 <二> 丹方改良:Spring 時代的 JavaWeb】之 Spring Boot 中的文件上傳與下載:實現文件管理功能

<前文回顧> 點擊此處查看 合集 https://blog.csdn.net/foyodesigner/category_12907601.html?fromshareblogcolumn&sharetypeblogcolumn&sharerId12907601&sharereferPC&sharesourceFoyoDesigner&sharefromfrom_link <今日更新> 一、開篇整…

搜索算法------DFS練習2

1. 題目 2. 思路和題解 從題目中可以看出&#xff0c;如果一個格子上有雨水&#xff0c;那么就可以流到周圍比他高度低的單元格&#xff0c;如果單元格和海洋相鄰&#xff0c;那么雨水也會流入海洋。總而言之一句話就是水從高處流向低處。從這里的流向可以聯想到深度優先搜索這…

[python] 正則表達式

1.分割str s"1-2--3---4" are.findall(r\d|[-],s) # 輸出&#xff1a;[1, -, 2, --, 3, ---, 4]s"-4(2(3)" # ? 表示 - 可以出現0次或1次 # \d 表示匹配一個或多個連續數字 # \D 表示匹配非數字字符 sre.findall(r-?\d|\D,s) # 輸出&#xff1a;[-4, (,…

定制化管理系統與通用管理系統,誰更勝一籌?

一、定制化管理系統與通用管理系統的定義與特點 定制化管理系統 定制化管理系統是根據企業的具體業務需求和流程進行個性化開發的軟件系統。它能夠深度貼合企業的管理需求&#xff0c;提供高度靈活的解決方案。其特點包括&#xff1a; 高度適應性&#xff1a;能夠精準匹配企業…

gitee 配置git上傳

Git入門&#xff1f;查看 幫助 , Visual Studio / TortoiseGit / Eclipse / Xcode 下如何連接本站, 如何導入倉庫 簡易的命令行入門教程: Git 全局設置: 以 176fuguM2項目為例 git config --global user.name "墮落圣甲蟲" git config --global user.email "11…

SpringBoot+Vue 中 WebSocket 的使用

WebSocket 是一種在單個 TCP 連接上進行全雙工通信的協議&#xff0c;它使得客戶端和服務器之間可以進行實時數據傳輸&#xff0c;打破了傳統 HTTP 協議請求 - 響應模式的限制。 下面我會展示在 SpringBoot Vue 中&#xff0c;使用WebSocket進行前后端通信。 后端 1、引入 j…