【再谈设计模式】享元模式~对象共享的优化妙手

一、引言

        在软件开发过程中,我们常常面临着创建大量细粒度对象的情况,这可能会导致内存占用过高、性能下降等问题。享元模式(Flyweight Pattern)就像是一位空间管理大师,它能够在不影响功能的前提下,有效地减少对象的数量,从而优化系统资源的使用。

二、定义与描述

        享元模式是一种结构型设计模式,它主要用于通过共享尽可能多的相似对象来减少内存使用和提高性能。其核心思想是将对象的状态分为内部状态(intrinsic state)和外部状态(extrinsic state)。内部状态是对象可共享的部分,它不会随环境的改变而改变;外部状态则是随环境变化而变化的部分,不能被共享。

三、抽象背景

        假设我们正在开发一个游戏,游戏中有许多相同类型的怪物,这些怪物可能只有一些属性(如生命值、攻击力等)的差异。如果我们为每个怪物都创建一个独立的对象,那么随着怪物数量的增加,内存的消耗将变得非常大。享元模式就可以用来解决这个问题,将怪物的通用属性(如外观、基本行为等内部状态)进行共享,而将每个怪物特有的属性(如当前生命值、当前攻击力等外部状态)单独处理。

四、适用场景与现实问题解决

  • 图形绘制系统
    • 在图形绘制系统中,可能需要绘制大量相同类型的图形,如圆形、矩形等。这些图形的形状(内部状态)是固定的,但它们的位置、颜色(外部状态)可能不同。通过享元模式,可以共享图形的形状对象,减少内存占用。

  • 文档编辑器中的字符处理
    • 文档编辑器中有大量的字符,每个字符的字体样式、大小等属性可能不同,但字符的基本形状(内部状态)是相同的。享元模式可以用来共享字符的基本形状对象。

五、享元模式的现实生活的例子

  • 汽车租赁公司
    • 汽车租赁公司有多种类型的汽车可供租赁,如轿车、SUV等。每一种类型的汽车(内部状态)是固定的,包括车型、座位数等。而汽车的颜色、当前里程数(外部状态)是随每一次租赁而变化的。租赁公司可以将相同类型的汽车看作是享元对象,共享汽车的基本信息,从而更好地管理车辆资源。

  • 咖啡店的咖啡杯
    • 咖啡店有不同种类的咖啡杯,如拿铁杯、卡布奇诺杯等。杯子的形状(内部状态)是固定的,但是杯子里咖啡的量、是否加糖(外部状态)是不同的。咖啡店可以将相同类型的杯子看作享元对象,共享杯子的基本形状信息。

六、初衷与问题解决

        初衷是为了减少内存中对象的数量,提高系统的性能和资源利用率。通过共享内部状态,避免了创建大量重复的对象,从而解决了因对象数量过多导致的内存占用过大和性能下降的问题。

七、代码示例

Java示例

类图:

  • FlyweightFactory 类有一个私有属性 flyweights(类型为 Map<String, Flyweight>)用于存储享元对象,并且有 getFlyweight 方法,根据传入的 key 来获取或创建具体的享元对象。
  • Flyweight 是抽象类,有受保护的属性 key,构造方法以及抽象方法 operation,定义了享元对象的基本结构和行为规范。
  • ConcreteFlyweight 类继承自 Flyweight 类,实现了自己的构造方法,并覆写了 operation 方法,用于提供具体的享元行为实现。

流程图:

        首先创建 FlyweightFactory 对象,然后两次调用 getFlyweight 方法来获取享元对象(第二次调用时会复用第一次创建的对象,因为已经存在对应 key 的对象了),最后分别调用获取到的享元对象的 operation 方法来执行具体操作。 

代码:

import java.util.HashMap;
import java.util.Map;// 享元工厂类
class FlyweightFactory {private Map<String, Flyweight> flyweights = new HashMap<>();public Flyweight getFlyweight(String key) {if (!flyweights.containsKey(key)) {flyweights.put(key, new ConcreteFlyweight(key));}return flyweights.get(key);}
}// 抽象享元类
abstract class Flyweight {protected String key;public Flyweight(String key) {this.key = key;}abstract void operation();
}// 具体享元类
class ConcreteFlyweight extends Flyweight {public ConcreteFlyweight(String key) {super(key);}@Overridevoid operation() {System.out.println("具体享元 " + key + " 被调用");}
}public class Main {public static void main(String[] args) {FlyweightFactory factory = new FlyweightFactory();Flyweight flyweight1 = factory.getFlyweight("A");Flyweight flyweight2 = factory.getFlyweight("A");flyweight1.operation();flyweight2.operation();}
}

C++示例

#include <iostream>
#include <unordered_map>// 抽象享元类
class Flyweight {
public:virtual void operation() = 0;virtual ~Flyweight() {}
};// 具体享元类
class ConcreteFlyweight : public Flyweight {
private:std::string key;
public:ConcreteFlyweight(std::string key) : key(key) {}void operation() override {std::cout << "具体享元 " << key << " 被调用" << std::endl;}
};// 享元工厂类
class FlyweightFactory {
private:std::unordered_map<std::string, Flyweight*> flyweights;
public:Flyweight* getFlyweight(std::string key) {if (flyweights.find(key) == flyweights.end()) {flyweights[key] = new ConcreteFlyweight(key);}return flyweights[key];}~FlyweightFactory() {for (auto it : flyweights) {delete it.second;}}
};int main() {FlyweightFactory factory;Flyweight* flyweight1 = factory.getFlyweight("A");Flyweight* flyweight2 = factory.getFlyweight("A");flyweight1->operation();flyweight2->operation();return 0;
}

Python示例

class FlyweightFactory:def __init__(self):self.flyweights = {}def get_flyweight(self, key):if key not in self.flyweights:self.flyweights[key] = ConcreteFlyweight(key)return self.flyweights[key]class Flyweight:def __init__(self, key):self.key = keydef operation(self):passclass ConcreteFlyweight(Flyweight):def operation(self):print(f"具体享元 {self.key} 被调用")if __name__ == "__main__":factory = FlyweightFactory()flyweight1 = factory.get_flyweight("A")flyweight2 = factory.get_flyweight("A")flyweight1.operation()flyweight2.operation()

Go示例

package mainimport ("fmt"
)// 抽象享元接口
type Flyweight interface {operation()
}// 具体享元结构体
type ConcreteFlyweight struct {key string
}func (cf *ConcreteFlyweight) operation() {fmt.Printf("具体享元 %s 被调用\n", cf.key)
}// 享元工厂结构体
type FlyweightFactory struct {flyweights map[string]Flyweight
}func NewFlyweightFactory() *FlyweightFactory {return &FlyweightFactory{flyweights: make(map[string]Flyweight),}
}func (ff *FlyweightFactory) getFlyweight(key string) Flyweight {if _, ok := ff.flyweights[key];!ok {ff.flyweights[key] = &ConcreteFlyweight{key}}return ff.flyweights[key]
}func main() {factory := NewFlyweightFactory()flyweight1 := factory.getFlyweight("A")flyweight2 := factory.getFlyweight("A")flyweight1.operation()flyweight2.operation()
}

八、享元模式的优缺点

  • 优点

    • 减少内存占用:通过共享对象,大大减少了创建对象所需的内存空间,特别是在处理大量相似对象时效果显著。
    • 提高性能:减少了对象的创建和销毁操作,从而提高了系统的运行速度。
    • 易于维护:将对象的内部状态和外部状态分离,使得代码结构更加清晰,易于理解和维护。
  • 缺点

    • 增加复杂性:需要额外的代码来管理享元对象的创建、共享和维护,这可能会增加系统的复杂性。
    • 外部状态管理:外部状态的处理需要额外的设计考虑,如果处理不当可能会导致逻辑混乱。

九、享元模式的升级版

        一种常见的升级版是组合享元模式(Composite Flyweight Pattern)。在这种模式下,享元对象可以组合成更复杂的结构。例如,在图形绘制系统中,不仅可以共享单个图形(如圆形、矩形)的享元对象,还可以将这些享元对象组合成更复杂的图形(如由多个圆形和矩形组成的复杂图案),而这个复杂图案本身也可以作为一个享元对象被共享。这样可以进一步提高系统的灵活性和资源利用率。

思维导图:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/496299.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

windos挂载目录到linux

验证环境麒麟V10 1: 在windows任意目录设置共享文件夹 2&#xff1a;记住网络路径\LAPTOP-86JV6NT1\gantie13_sdk 在linux中替换为本机ip级相对路径 比如本级ip是192.168.23.23&#xff0c;linux环境需要ping通本地地址 3&#xff1a; sudo apt-get install cifs-utils sud…

基于STM32单片机矿井矿工作业安全监测设计

基于STM32单片机矿井矿工作业安全监测设计 目录 项目开发背景设计实现的功能项目硬件模块组成设计思路系统功能总结使用的模块技术详情介绍总结 1. 项目开发背景 随着矿井矿工作业环境的复杂性和危险性逐渐增加&#xff0c;矿井作业安全问题引起了社会各界的广泛关注。传统的…

WebRTC服务质量(11)- Pacer机制(03) IntervalBudget

WebRTC服务质量&#xff08;01&#xff09;- Qos概述 WebRTC服务质量&#xff08;02&#xff09;- RTP协议 WebRTC服务质量&#xff08;03&#xff09;- RTCP协议 WebRTC服务质量&#xff08;04&#xff09;- 重传机制&#xff08;01) RTX NACK概述 WebRTC服务质量&#xff08;…

Java爬虫技术:按关键字搜索VIP商品详情

在数字化时代&#xff0c;电子商务平台的竞争日益激烈&#xff0c;而精准的数据采集和分析成为了企业获取竞争优势的关键。对于电商平台而言&#xff0c;能够根据用户输入的关键字快速搜索并展示VIP商品的详细信息&#xff0c;不仅能够提升用户体验&#xff0c;还能够增加销售机…

若依框架中的上传图片后如何实现回显到页面的

在日常开发中&#xff0c;总会遇到上传文件、图片等功能&#xff0c;然后本地开发的话&#xff0c;又没有像OSS、七牛等网络存储&#xff0c;这个时候通常将文件上传到本地&#xff0c;那么上传之后拿到的是本地的路径&#xff0c;存储到数据库中&#xff0c;查询的时候如何将本…

一键图片转3D模型,AI建模,一键把图片转三维模型,二维图片转3维模型,AI建模

一键图片转3D模型&#xff0c;AI建模&#xff0c;一键把图片转三维模型&#xff0c;二维图片转3维模型,AI建模&#xff0c;公测版&#xff0c;每天不定时免费开放&#xff0c;非常强大 1咱们先打开ai.glbxz.com http://ai.glbxz.com 22 2导入图片。支持单张和多张图片生成 3…

梳理你的思路(从OOP到架构设计)_设计模式Android + Composite模式

目录 1、Android Composite模式 2、范例之一 3、范例之二 1、Android Composite模式 在Android平台里&#xff0c;像Button或ImageButton等屏幕控件皆通称为View。多个View能组合在一起&#xff0c;就会各种排列方式&#xff0c;即称为「布局」 (Layout)。这Layout类别就是…

LabVIEW软件项目设计方案如何制定

制定LabVIEW软件项目设计方案需要综合考虑需求分析、架构设计、功能模块划分和时间预算等多个方面&#xff0c;确保项目开发过程高效、可控且最终满足目标要求。以下是一个详细的制定流程&#xff1a; ​ 1. 需求分析 目标定义&#xff1a;明确项目的目标&#xff0c;例如数据采…

机器学习(二)-简单线性回归

文章目录 1. 简单线性回归理论2. python通过简单线性回归预测房价2.1 预测数据2.2导入标准库2.3 导入数据2.4 划分数据集2.5 导入线性回归模块2.6 对测试集进行预测2.7 计算均方误差 J2.8 计算参数 w0、w12.9 可视化训练集拟合结果2.10 可视化测试集拟合结果2.11 保存模型2.12 …

Linux运维常见命令

vi/vim快捷键使用 1)拷贝当前行 yy ,拷贝当前行向下的5行 5yy&#xff0c;并粘贴&#xff08;输入p&#xff09;。 2)删除当前行 dd ,删除当前行向下的5行5dd 3)在文件中查找某个单词 [命令行下 /关键字&#xff0c;回车查找 ,输入n就是查找下一个 ] 4)设置文件的行号&…

MacOS下TestHubo安装配置指南

TestHubo是一款开源免费的测试管理工具&#xff0c; 下面介绍MacOS私有部署的安装与配置。TestHubo 私有部署版本更适合有严格数据安全要求的企业&#xff0c;支持在本地或专属服务器上运行&#xff0c;以实现对数据和系统的完全控制。 1、Mac 服务端安装 Mac安装包下载地址&a…

jumpserver docker安装

#安装jumpserver最新版本&#xff08;当前最新版本v4.5.0-ce&#xff09; curl -sSL https://resource.fit2cloud.com/jumpserver/jumpserver/releases/latest/download/quick_start.sh | bash#登录 http://192.168.31.168/ 默认账号密码 admin/ChangeMe 修改后&#xff1a; ad…

VBA技术资料MF243:利用第三方软件复制PDF数据到EXCEL

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…

LabVIEW生物医学信号虚拟实验平台

介绍了一款基于LabVIEW的多功能生物医学信号处理实验平台的设计和实现。平台通过实践活动加强学生对理论的理解和应用能力&#xff0c;特别是在心电图(ECG)和脑电图(EEG)的信号处理方面。实验平台包括信号的滤波、特征提取和频谱分析等功能&#xff0c;能直观体验和掌握生物医学…

json字符串或者json文件转换成相应的bean,报错“Unrecognized field xxx , not marked as ignorable”

1. 异常描述 将一个json字符串或者json文件转换成相应的bean的时候&#xff0c;报如下错误&#xff1a; 2. 异常分析 bean中某个字段的get和set方法可能不是工具自动生成的&#xff0c;而是自己写的&#xff0c;譬如字段是“sInfo”&#xff0c;本来get方法是应该写成getsI…

Postman接口测试01|接口测试基础概念、http协议、RESTful风格、接口文档

目录 一、接口测试基础概念 1、什么是接口 2、接口的类型 3、什么是接口测试 4、为什么要做接口测试 5、接口测试的实现方式 6、什么是自动化接口测试&#xff1f; 二、接口返回的数据格式 1、三种格式 2、Json 三、接口协议 1、webservice协议 2、dubbo协议 3、…

游戏引擎学习第62天

回顾 我们目前正在开发一把虚拟剑&#xff0c;目的是让角色可以用这把剑进行攻击。最初的工作中&#xff0c;我们使用了一个摇滚位图作为虚拟剑的模型&#xff0c;并且实现了一个基本的功能&#xff1a;角色可以丢下剑。但这个功能并没有达到预期的效果&#xff0c;因为我们想…

AAAI-2024 | 大语言模型赋能导航决策!NavGPT:基于大模型显式推理的视觉语言导航

作者&#xff1a;Gengze Zhou, Yicong Hong, Qi Wu 单位&#xff1a;阿德莱德大学&#xff0c;澳大利亚国立大学 论文链接&#xff1a; NavGPT: Explicit Reasoning in Vision-and-Language Navigation with Large Language Models &#xff08;https://ojs.aaai.org/index.p…

商品线上个性定制,并实时预览3D定制效果,是如何实现的?

商品线上3D个性化定制的实现涉及多个环节和技术&#xff0c;以下是详细的解释&#xff1a; 一、实现流程 产品3D建模&#xff1a; 是实现3D可视化定制的前提&#xff0c;需要对产品进行三维建模。可通过三维扫描仪或建模师进行建模&#xff0c;将产品的外观、结构、材质等细…

JS 异步 ( 一、异步概念、Web worker 基本使用 )

文章目录 异步代码异步执行概念ES6 之前的异步 Web worker 异步 代码异步执行概念 通常代码是自上而下同步执行的&#xff0c;既后面的代码必须等待前面的代码执行完才会执行&#xff0c;而异步执行则是将主线程中的某段代码交由子线程去执行&#xff0c;当交给子线程后&…