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

一、引言

        在软件开发过程中,我们常常面临着创建大量细粒度对象的情况,这可能会导致内存占用过高、性能下降等问题。享元模式(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.mzph.cn/web/64148.shtml

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

相关文章

Milvus×EasyAi:如何用java从零搭建人脸识别应用

如何从零搭建一个人脸识别应用&#xff1f;不妨试试原生Java人工智能算法&#xff1a;EasyAi Milvus 的组合拳。 本文将使用到的软件和工具包括&#xff1a; EasyAi&#xff1a;人脸特征向量提取Milvus&#xff1a;向量数据库用于高效存储和检索数据。 01. EasyAi&#xff1a;…

NS3学习——tcpVegas算法代码详解(2)

NS3学习——tcpVegas算法代码详解&#xff08;1&#xff09;-CSDN博客 目录 4.TcpVegas类中成员函数 (5) CongestionStateSet函数 (6) IncreaseWindow函数 1.检查是否启用 Vgas 2.判断是否完成了一个“Vegas 周期” 2.1--if&#xff1a;判断RTT样本数量是否足够 2.2--e…

GitLab 将停止为中国区用户提供服务,60天迁移期如何应对? | LeetTalk Daily

“LeetTalk Daily”&#xff0c;每日科技前沿&#xff0c;由LeetTools AI精心筛选&#xff0c;为您带来最新鲜、最具洞察力的科技新闻。 GitLab作为一个广受欢迎的开源代码托管平台&#xff0c;近期宣布将停止服务中国大陆、澳门和香港地区的用户提供服务。根据官方通知&#x…

华为实训课笔记 2024 1223-1224

华为实训 12/2312/24 12/23 [Huawei]stp enable --开启STP display stp brief --查询STP MSTID Port Role STP State Protection 实例ID 端口 端口角色 端口状态 是否开启保护[Huawei]display stp vlan xxxx --查询制定vlan的生成树计算结…

《Java源力物语》-3.空值猎手

~犬&#x1f4f0;余~ “我欲贱而贵&#xff0c;愚而智&#xff0c;贫而富&#xff0c;可乎&#xff1f; 曰&#xff1a;其唯学乎” \quad 夜色渐深&#xff0c;在一处偏僻小径上&#xff0c;月光透过浓密的源力云层&#xff0c;在地面上投下斑驳的光影。String正独自练习着刚从…

科技云报到:人工智能时代“三大件”:生成式AI、数据、云服务

科技云报到原创。 就像自行车、手表和缝纫机是工业时代的“三大件”。生成式AI、数据、云服务正在成为智能时代的“新三大件”。加之全球人工智能新基建加速建设&#xff0c;成为了人类社会数字化迁徙的助推剂&#xff0c;让新三大件之间的耦合越来越紧密。从物理世界到数字世…

hiprint结合vue2项目实现静默打印详细使用步骤

代码地址是&#xff1a;vue-plugin-hiprint: hiprint for Vue2/Vue3 ⚡打印、打印设计、可视化设计器、报表设计、元素编辑、可视化打印编辑 本地安装包地址&#xff1a;electron-hiprint 发行版 - Gitee.com 1、先安装hipint安装包在本地 2、项目运行npm&#xff08;socket.…

CUDA各种内存和使用方法

文章目录 1、全局内存2、局部内存3、共享内存3.1 静态共享内存3.2 动态共享内存 4、纹理内存5、常量内存6、寄存器内存7、用CUDA运行时API函数查询设备CUDA 错误检测 1、全局内存 特点&#xff1a;容量最大&#xff0c;访问延时最大&#xff0c;所有线程都可以访问。 线性内存…

Chapter 03 复合数据类型-1

1.列表 Python内置的一种有序、可变的序列数据类型&#xff1b; 列表的定义&#xff1a; [ ]括起来的逗号分隔的多个元素组成的序列 列表对象的创建&#xff1a; &#xff08;1&#xff09;直接赋值 >>> list1 []#创建一个空列表赋值给list1 >>> list…

【后端】LNMP环境搭建

长期更新各种好文&#xff0c;建议关注收藏&#xff01; 本文近期更新完毕。 LNMPlinuxnginxmysqlphp 需要的资源 linux服务器 web服务软件nginx 对应的语言编译器代码文件 数据库mysql安装 tar.gz包或者命令行安装 进入root&#xff1a; sodu 或su mkdir path/{server,soft}…

基于PyQt5的UI界面开发——多界面切换

介绍 最初&#xff0c;因为课设的缘故&#xff0c;我只是想做一个通过按键进行切面切换而已&#xff0c;但是我看网上资料里面仅是语焉不详&#xff0c;让我困惑的很&#xff0c;但后面我通过摸索才发现这件事实在是太简单了&#xff0c;因此我想要记录下来。 本博客将介绍如…

操作002:HelloWorld

文章目录 操作002&#xff1a;HelloWorld一、目标二、具体操作1、创建Java工程①消息发送端&#xff08;生产者&#xff09;②消息接收端&#xff08;消费者&#xff09;③添加依赖 2、发送消息①Java代码②查看效果 3、接收消息①Java代码②控制台打印③查看后台管理界面 操作…

机器视觉检测相机基础知识 | 颜色 | 光源 | 镜头 | 分辨率 / 精度 / 公差

注&#xff1a;本文为 “keyence 视觉沙龙中机器视觉检测基础知识” 文章合辑。 机器视觉检测基础知识&#xff08;一&#xff09;颜色篇 视觉检测硬件构成的基本部分包括&#xff1a;处理器、相机、镜头、光源。 其中&#xff0c;和光源相关的最重要的两个参数就是光源颜色和…

【体验官招募】SoFlu - JavaAI 开发助手:开启智能开发新时代

你是否有过这样的经历&#xff1f;在深夜的办公室里&#xff0c;面对紧急的 Java 项目&#xff0c;看着厚厚的需求文档&#xff0c;你是否感到无从下手&#xff1f; 当你尝试理解客户那些复杂又模糊的需求时&#xff0c;是否会因为要和产品经理反复沟通确认每一个细节而感到厌…

自学记录HarmonyOS Next DRM API 13:构建安全的数字内容保护系统

在完成了HarmonyOS Camera API的开发之后&#xff0c;我开始关注更复杂的系统级功能。在浏览HarmonyOS Next文档时&#xff0c;我发现了一个非常有趣的领域&#xff1a;数字版权管理&#xff08;DRM&#xff09;。最新的DRM API 13提供了强大的工具&#xff0c;用于保护数字内容…

【HENU】河南大学计院2024 操作系统 简答题复习

和光同尘_我的个人主页 一直游到海水变蓝。 单项选择 15x2 30 判断 10x1 10 简答 3x10 30 综合 3x10 30 简答题 简述操作系统的四个基本特征。 并发性 共享性 虚拟性 异步性 并发性是最重要特性&#xff0c;其它三种特性以此为前提。 并发 并发(Concurrence)&#…

GEE错误——PCA系数变换的时候出现的错误

目录 错误提示1 错误提示2 原始的教程链接&#xff1a; 错误代码 修正后的代码 结果 错误提示1 这个是因为原始GEE教程中给的让我们填入需要进行计算的波段名称&#xff0c;而且是以list的形式传入。 错误提示2 这里我们虽然传入了正确的波段名称&#xff0c;但是发现要…

C#代码实现把中文录音文件(.mp3 .wav)转为文本文字内容

我们有一个中文录音文件.mp3格式或者是.wav格式&#xff0c;如果我们想要提取录音文件中的文字内容&#xff0c;我们可以采用以下方法&#xff0c;不需要使用Azure Speech API 密钥注册通过离线的方式实现。 1.首先我们先在NuGet中下载两个包 NAudio 2.2.1、Whisper.net 1.7.3…

计算机操作系统与安全复习笔记

1 绪论 操作系统目标: 方便性; 有效性; 可扩充性; 开放性. 作用: 用户与计算机硬件系统之间的接口; 计算机资源的管理者; 实现了对计算机资源的抽象; 计算机工作流程的组织者. 多道程序设计: 内存中同时存放若干个作业, 使其共享系统资源且同时运行; 单处理机环境下宏观上并行…

qt5.12.11+msvc编译器编译qoci驱动

1.之前编译过minGW编译器编译qoci驱动,很顺利就完成了,文章地址:minGW编译qoci驱动详解,今天按照之前的步骤使用msvc编译器进行编译,直接就报错了: 查了些资料,发现两个编译器在编译时,pro文件中引用的库不一样,下面是msvc编译器引用的库,其中编译引用的库我这里安装…