設計模式:享元模式 Flyweight

目錄

  • 前言
  • 問題
  • 解決方案
  • 享元工廠
  • 結構
  • 代碼


前言

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


問題

假如你希望在長時間工作后放松一下, 所以開發了一款簡單的游戲: 玩家們在地圖上移動并相互射擊。 你決定實現一個真實的粒子系統,并將其作為游戲的特色。大量的子彈、導彈和爆炸彈片會在整個地圖上穿行, 為玩家提供緊張刺激的游戲體驗。

開發完成后, 你推送提交了最新版本的程序, 并在編譯游戲后將其發送給了一個朋友進行測試。 盡管該游戲在你的電腦上完美運行, 但是你的朋友卻無法長時間進行游戲: 游戲總是會在他的電腦上運行幾分鐘后崩潰。 在研究了幾個小時的調試消息記錄后, 你發現導致游戲崩潰的原因是內存容量不足。 朋友的設備性能遠比不上你的電腦, 因此游戲運行在他的電腦上時很快就會出現問題。

真正的問題與粒子系統有關。 每個粒子(一顆子彈、 一枚導彈或一塊彈片)都由包含完整數據的獨立對象來表示。 當玩家在游戲中鏖戰進入高潮后的某一時刻, 游戲將無法在剩余內存中載入新建粒子,于是程序就崩潰了。
在這里插入圖片描述

解決方案

仔細觀察 粒子 Particle 類, 你可能會注意到 顏色(color)和 精靈圖(sprite)這兩個成員變量所消耗的內存要比其他變量多得多。更糟糕的是,對于所有的粒子來說,這兩個成員變量所存儲的數據幾乎完全一樣(比如所有子彈的顏色和精靈圖都一樣)。

在這里插入圖片描述
每個粒子的另一些狀態(坐標、 移動矢量和速度)則是不同的。 因為這些成員變量的數值會不斷變化。 這些數據代表粒子在存續期間不斷變化的情景, 但每個粒子的顏色和精靈圖則會保持不變。

對象的常量數據通常被稱為內在狀態, 其位于對象中, 其他對象只能讀取但不能修改其數值。 而對象的其他狀態常常能被其他對象“從外部”改變,因此被稱為外在狀態。

享元模式建議不在對象中存儲外在狀態, 而是將其傳遞給依賴于它的一個特殊方法。 程序只在對象中保存內在狀態, 以方便在不同情景下重用。 這些對象的區別僅在于其內在狀態(與外在狀態相比, 內在狀態的變體要少很多), 因此你所需的對象數量會大大削減。
在這里插入圖片描述
讓我們回到游戲中。 假如能從粒子類中抽出外在狀態, 那么我們只需三個不同的對象(子彈、 導彈和彈片)就能表示游戲中的所有粒子。 你現在很可能已經猜到了, 我們將這樣一個僅存儲內在狀態的對象稱為享元。

享元工廠

為了能更方便地訪問各種享元, 你可以創建一個工廠方法來管理已有享元對象的緩存池。 工廠方法從客戶端處接收目標享元對象的內在狀態作為參數, 如果它能在緩存池中找到所需享元,則將其返回給客戶端;如果沒有找到,它就會新建一個享元,并將其添加到緩存池中。

你可以選擇在程序的不同地方放入該函數。 最簡單的選擇就是將其放置在享元容器中。 除此之外, 你還可以新建一個工廠類, 或者創建一個靜態的工廠方法并將其放入實際的享元類中。

結構

在這里插入圖片描述

代碼

Tip: 享元工廠內含在Game類里面

#include <iostream>
#include <string>
#include <vector>
#include <memory>
using namespace std;class Particle {
public:Particle(const string& color, const string& spirit) : m_color(color), m_spirit(spirit) {}// 重載運算符==bool operator==(const Particle& other) const {return m_color == other.m_color && m_spirit == other.m_spirit;}void move(const string& coords, const string& vector, const string& speed) {cout << "particle is moving to " << coords << " in " << vector << " " << speed << endl;}void draw(const string& coords, const string& canvas) {cout << "draw the particle at " << coords << " in " << canvas << endl;}
protected:string m_color;string m_spirit;
};class MovingParticle : public Particle {
public:MovingParticle(shared_ptr<Particle> particle, const string& color, const string& spirit, const string& coords, const string& vector, const string& speed) :Particle(color, spirit), m_particle(particle), m_coords(coords), m_vector(vector), m_speed(speed) {}void move() {m_particle->move(m_coords, m_vector, m_speed);}void draw(const string& canvas) {m_particle->draw(m_coords, canvas);}
private:shared_ptr<Particle> m_particle;string m_coords;string m_vector;string m_speed;
};class Game {
public:vector<shared_ptr<MovingParticle>> mps;void addParticle(const string& coords, const string& vector, const string& speed, const string& color, const string& spirit) {auto temp = make_shared<Particle>(color, spirit);bool exists = false;for (auto m_particle : m_particles) {if (*m_particle == *temp) {exists = true;break;}}if (!exists) {m_particles.push_back(make_shared<Particle>(color, spirit));}mps.push_back(make_shared<MovingParticle>(temp, color, spirit, coords, vector, speed));}void draw(const string& canvas) {for (auto mp : mps) {mp->draw(canvas);}}
private:vector<shared_ptr<Particle>> m_particles;
};class Unit {
public:// 坦克屬性:名稱、位置、武器威力、自身精靈圖、子彈顏色、子彈精靈圖Unit(Game* game, const string& name, const string& position, const string& weaponPower, const string& tankSpirit, const string& bulletColor, const string& bulletSpirit) :m_game(game), m_name(name), m_position(position), m_weaponPower(weaponPower), m_tankSpirit(tankSpirit), m_bulletColor(bulletColor), m_bulletSpirit(bulletSpirit) {}virtual ~Unit() = default;// 發射時只需傳入目標對象,子彈樣式已在構造時確定virtual void fireAt(Unit& target) {// 使用目標當前位置和坦克自身的子彈屬性m_game->addParticle(target.getPosition(), "forward", m_weaponPower, m_bulletColor, m_bulletSpirit);cout << m_name << " fires at " << target.m_name << " (position: " << target.getPosition() << ")" << " with " << m_bulletColor << " " << m_bulletSpirit << endl;}string getPosition() const {return m_position;}void moveTo(const string& newPosition) {m_position = newPosition;cout << m_name << " moved to " << newPosition << endl;}// 獲取坦克自身精靈圖string getTankSpirit() const {return m_tankSpirit;}private:Game* m_game;string m_name;         // 坦克名稱string m_position;     // 坦克位置string m_weaponPower;  // 武器威力(影響子彈速度等)string m_tankSpirit;   // 坦克自身精靈圖string m_bulletColor;  // 子彈顏色(構造時確定,固定不變)string m_bulletSpirit; // 子彈精靈圖(構造時確定,固定不變)
};// 主函數用于測試
int main() {Game game;// 創建坦克單位,構造時指定子彈樣式(最后兩個參數)Unit tank1(&game, "Tank A", "base1", "high_speed", "tank_red.png", "red", "bullet_round.png");Unit tank2(&game, "Tank B", "hill2", "normal", "tank_blue.png", "blue", "bullet_laser.png");// 攻擊:只需傳入目標對象tank1.fireAt(tank2);// 畫出子彈game.draw("battlefield");// 坦克2移動位置躲避tank2.moveTo("valley3");return 0;
}

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

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

相關文章

Spring Boot容器化實戰:用官方OpenJDK鏡像極速啟動你的應用

前言 用 Docker 打包 Java 應用,尤其是 Spring Boot,簡直是開發者的超級利器。想象一下,你的程序就像勤快的外賣小哥,隨時待命,跑遍任何一臺機器,馬上為你服務。不論是開發環境還是生產環境,Docker 都能讓部署變得輕松又高效,徹底告別“環境不一致”的煩惱。 本篇文章…

【計算機網絡 | 第1篇】計算機網絡概述(上)

文章目錄一.現代通信基礎&#x1f95d;二.網絡、互聯網、英特網&#x1f9fe;1.網絡&#xff08;Network&#xff09;2.互聯網&#xff08;internet&#xff09;3.因特網&#xff08;Internet&#xff09;三.計算機網絡的標準定義&#x1f95d;早期定義&#x1f9fe;物理構成視…

python語法筆記

問題解決辦法 原本是個小問題&#xff0c;但是花了我大量時間。先說最后的解決辦法&#xff1a;360網絡急救箱搞的。一.問題描述 始終拉取失敗 二.解決過程 1.登陸憑證檢測&#xff0c;查下密碼是不是不對。2.清除GIT所有數據 3.使用SSH拉取 生成密鑰網站上添加密鑰SSH 拉取4.G…

XTOM藍光三維掃描儀:解鎖中小尺寸復雜零件的高精度3D檢測新境界

在3C消費電子行業&#xff0c;產品從出廠到用戶手中&#xff0c;可能經歷運輸、使用中的意外跌落。據統計&#xff0c;超過30%的電子產品售后問題與物理沖擊相關。跌落測試可模擬產品在運輸、使用中意外跌落的場景&#xff0c;可評估其結構強度、內部組件抗沖擊能力&#xff0c…

Django+celery異步:拿來即用,可移植性高

一、依賴環境 1、python解釋器版本&#xff1a;python3.7.5 2、穩定依賴包 # Celery 核心 celery5.2.7 kombu5.2.4 billiard3.6.4.0 vine5.0.0# Redis broker backend redis4.3.6# eventlet (如果用 -P eventlet)【windows系統可以使用】 eventlet0.33.3 greenlet1.1.3# 避免…

Ubuntu18.04 LTS +RTL 8125 出現安裝完系統后沒有網絡問題

Ubuntu18.04 LTS RTL 8125 出現安裝完系統后沒有網絡問題問題描述最終解決方案1.下載對應的Realtek網卡驅動&#xff0c;使用命令lspci查看網卡信息安裝網卡3.重啟電腦記錄過程1.內核升級方式1&#xff09;下載新的內核驅動2&#xff09;安裝內核驅動3&#xff09;重啟電腦4&am…

集成電路學習:什么是ARM CortexM處理器核心

ARM Cortex-M是ARM公司專為微控制器( Microcontroller)設計的處理器核心系列,它以其高性能、低功耗和易于開發的特點,在嵌入式系統和微控制器領域得到了廣泛應用。以下是關于ARM Cortex-M的詳細介紹: 一、ARM Cortex-M的概述 ARM Cortex-M系列處理器是基于ARM架構的高能效…

Apache Ignite 的分布式原子類型(Atomic Types)

以下的內容是關于 Apache Ignite 的分布式原子類型&#xff08;Atomic Types&#xff09;&#xff0c;主要包括 IgniteAtomicLong 和 IgniteAtomicReference。它們是 跨集群節點的“全局共享變量”&#xff0c;支持線程安全、原子性操作&#xff0c;即使多個節點同時訪問也能保…

Leetcode 08 java

283. 移動零 提示 給定一個數組 nums&#xff0c;編寫一個函數將所有 0 移動到數組的末尾&#xff0c;同時保持非零元素的相對順序。 請注意 &#xff0c;必須在不復制數組的情況下原地對數組進行操作。 示例 1: 輸入: nums [0,1,0,3,12] 輸出: [1,3,12,0,0] 示例 2: 輸…

LeetCode 56 - 合并區間

思路 排序&#xff1a;將所有區間按起始點從小到大排序。貪心合并&#xff1a;初始化一個結果列表&#xff0c;放入第一個區間。然后遍歷剩余區間&#xff0c;將當前區間與結果列表中的最后一個區間比較&#xff1a; 若重疊&#xff08;當前區間起點 ≤ 結果區間終點&#xff0…

DNS 正向查找與反向查找

DNS 區域是 DNS 中基本的組織單元&#xff0c;為域名定義了管理和權威邊界。一個 DNS 區域通常包含一系列 DNS 資源記錄&#xff0c;包括名稱到地址的映射&#xff08;正向查找&#xff09;和地址到名稱的映射&#xff08;反向查找&#xff09;。這些區域對于高效管理和解析網絡…

Oracle ERP FORM開發 — 新增查詢條件

1 根據值來查詢具體流程步驟看第2節&#xff0c;這里提供核心的增加查詢條件的觸發器代碼&#xff1a;1.1 可完全匹配的值比如“是”&#xff0c;“否”&#xff0c;“物料”&#xff0c;“”汽車 等等這些可以直接通過對應的值匹配&#xff0c;特點就是詞語&#xff0c;短小。…

Flutter實現列表功能

在Flutter中,可以通過ListView和ListTile等組件來實現類似Android中RecyclerView和Adapter的功能。以下是一個通用的設計架構,用于設計列表數據: 1. 定義數據模型 首先,定義一個數據模型類,用于存儲列表中每一項的數據。例如: class ItemModel {final String title;fi…

2.1、Redis的單線程本質和多線程的操作

Redis的單線程本質 1. 核心單線程部分 #mermaid-svg-iFErSltthPIEsuiP {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-iFErSltthPIEsuiP .error-icon{fill:#552222;}#mermaid-svg-iFErSltthPIEsuiP .error-text{fil…

文件權限值的表示方法

文章目錄字符表示方法8 進制數值表示方法字符表示方法 Linux表示說明Linux表示說明r--只讀-w-僅可寫--x僅可執行rw-可讀可寫-wx可寫可執行r-x可讀可執行rwx可讀可寫可執行---無權限 8 進制數值表示方法 權限符號8進制2進制r4100w2010x1001rw6110rx5101wx3011rwx7111---0000

【38】WinForm入門到精通 ——WinForm平臺為AnyCPU 無法切換為x64,也無法添加 x64及其他平臺

WinForm 是 Windows Form 的簡稱&#xff0c;是基于 .NET Framework 平臺的客戶端&#xff08;PC軟件&#xff09;開發技術&#xff0c;是 C# 語言中的一個重要應用。.NET 提供了大量 Windows 風格的控件和事件&#xff0c;可以直接拿來使用。本專欄內容是按照標題序號逐漸深入…

門控激活函數:GLU/GTU/Swish/HSwish/Mish/SwiGLU

10 門控激活函數 10.1 GLU&#xff1a;門控線性單元函數Gated Linear Unit10.2 GTU&#xff1a;門控Tanh單元函數Gated Tanh Unit自門控激活函數&#xff08;Self-gated activation function&#xff09;是一種通過自身機制動態調節信息流動的激活函數&#xff0c;其核心在于模…

Linux內核IPv4多播路由深度解析:從數據結構到高效轉發

多播路由是網絡通信的核心技術之一&#xff0c;Linux內核通過精密的多層設計實現了高性能的IPv4多播路由功能。本文將深入剖析其核心實現機制&#xff0c;揭示其高效運轉的秘密。一、核心數據結構&#xff1a;路由系統的基石1. 多播路由表&#xff08;struct mr_table&#xff…

ffmpeg-7.1.1 下載安裝 windows 版,MP4 轉 m3u8 切片,遇到報錯 Unrecognized option ‘vbsf‘的解決辦法

工作中偶爾會需要造指定大小的文檔文件&#xff0c;不要求內容&#xff0c;可以隨意填充任意無毒內容&#xff0c;所以打算用ts文件填充&#xff0c;現記錄下過程。一、下載 ffmpeg廢話不多說&#xff0c;上鏈接&#xff0c;https://ffmpeg.org/會跳轉新頁面&#xff0c;向下拉…

Linux網絡編程:網絡基礎概念(下)

目錄 前言&#xff1a; 一、網絡傳輸基本流程 1.1、認識MAC地址 1.2、認識IP地址 二、socket編程預備 2.1、端口號 2.2、傳輸層的代表 2.3、網絡字節序 2.4、sockaddr 結構 總結&#xff1a; 前言&#xff1a; 大家好&#xff0c;上一篇文章&#xff0c;我們說到了…