目录

  • 前言
  • 问题
  • 解决方案
  • 享元工厂
  • 结构
  • 代码


前言

享元是一种结构型设计模式,它摒弃了在每个对象中保存所有数据的方式,通过共享多个对象所共有的相同状态,让你能在有限的内存容量中载入更多对象。


问题

假如你希望在长时间工作后放松一下, 所以开发了一款简单的游戏: 玩家们在地图上移动并相互射击。 你决定实现一个真实的粒子系统,并将其作为游戏的特色。大量的子弹、导弹和爆炸弹片会在整个地图上穿行, 为玩家提供紧张刺激的游戏体验。

开发完成后, 你推送提交了最新版本的程序, 并在编译游戏后将其发送给了一个朋友进行测试。 尽管该游戏在你的电脑上完美运行, 但是你的朋友却无法长时间进行游戏: 游戏总是会在他的电脑上运行几分钟后崩溃。 在研究了几个小时的调试消息记录后, 你发现导致游戏崩溃的原因是内存容量不足。 朋友的设备性能远比不上你的电脑, 因此游戏运行在他的电脑上时很快就会出现问题。

真正的问题与粒子系统有关。 每个粒子(一颗子弹、 一枚导弹或一块弹片)都由包含完整数据的独立对象来表示。 当玩家在游戏中鏖战进入高潮后的某一时刻, 游戏将无法在剩余内存中载入新建粒子,于是程序就崩溃了。
在这里插入图片描述

解决方案

仔细观察 粒子 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;我们说到了…