当前位置: 首页 > article >正文

11.享元模式 (Flyweight)

定义

Flyweight 模式(享元模式) 是一种结构型设计模式,它旨在通过共享对象来有效支持大量细粒度对象的复用。该模式主要通过共享细节来减少内存使用,提升性能,尤其在需要大量对象时非常有效。

基本思想:

共享对象:对象的内存资源和属性可以共享,从而减少内存消耗。
外部状态与内部状态的区分:
内部状态:在对象内部存储的属性,通常是共享的,可以被多个对象共享。
外部状态:依赖于具体的上下文环境,通常是每个对象独有的,不共享的。

Flyweight 模式的核心目标是:

  • 共享和复用:将对象的共享部分提取出来,以减少内存消耗。
  • 提高性能:通过避免重复创建大量相似的对象来提升性能。

Flyweight 模式适用于以下场景:

需要大量对象:当一个系统需要大量相似对象时,使用 Flyweight 模式可以减少内存开销。
对象细粒度重复:系统中很多对象的内部状态相同,只有外部状态不同。
资源有限的环境:在内存或者其他资源有限的环境中,Flyweight 可以有效降低资源消耗。
对象状态可分离:可以将对象的状态分为内部状态和外部状态。

常见应用场景:

图形应用:多个相同的图形(如圆形、矩形)共享相同的属性。
文字渲染:不同的字符共享相同的字体样式、大小等。

类设计

Flyweight 模式通常包括以下角色:

Flyweight(享元):定义共享对象的接口。
ConcreteFlyweight(具体享元):实现 Flyweight 接口,存储共享的状态。
FlyweightFactory(享元工厂):管理享元对象池,确保对象复用。
Client(客户端):使用享元对象,区分内部和外部状态。


#include <iostream>
#include <map>
#include <string>
using namespace std;

class Font {
private:
    string key;  // 内部状态:唯一的字体标识符(共享部分)
    // 其他内部状态(例如字体样式、字体颜色等)
    
public:
    Font(const string& key) : key(key) {
        cout << "Font created with key: " << key << endl;
    }

    void display(const string& text) {
        // 外部状态:文本的内容可以不同
        cout << "Displaying font " << key << " for text: " << text << endl;
    }
};

class FontFactory {
private:
    map<string, Font*> fontPool;  // 享元池:管理字体对象(共享的内部状态)
    
public:
    Font* GetFont(const string& key) {
        map<string, Font*>::iterator item = fontPool.find(key);
        
        if (item != fontPool.end()) {
            // 如果享元池中有对应的字体,直接返回
            cout << "Reusing existing font: " << key << endl;
            return item->second;
        } else {
            // 如果没有对应字体,创建新的字体对象
            Font* font = new Font(key);
            fontPool[key] = font;
            return font;
        }
    }

    void clear() {
        for (auto& item : fontPool) {
            delete item.second;
        }
        fontPool.clear();
    }
};

int main() {
    FontFactory fontFactory;

    // 获取并显示字体
    Font* font1 = fontFactory.GetFont("Arial");
    font1->display("Hello World!");

    Font* font2 = fontFactory.GetFont("Arial");
    font2->display("Hello again!");

    Font* font3 = fontFactory.GetFont("Times New Roman");
    font3->display("New text!");

    // 享元池重用 "Arial" 字体
    Font* font4 = fontFactory.GetFont("Arial");
    font4->display("Reusing Arial");

    // 清理资源
    fontFactory.clear();

    return 0;
}

运行代码后输出如下:

Font created with key: Arial
Displaying font Arial for text: Hello World!
Reusing existing font: Arial
Displaying font Arial for text: Hello again!
Font created with key: Times New Roman
Displaying font Times New Roman for text: New text!
Reusing existing font: Arial
Displaying font Arial for text: Reusing Arial

从输出可以看到:
对于 Arial 字体,第一次调用时创建了字体对象。
第二次获取 Arial 字体时,直接复用了已经创建的对象。
对于 Times New Roman,则是新创建的字体对象。

类设计分析

  1. Font 类(享元类)
    Font 类代表一个字体对象,它有一个 key 属性作为内部状态。这是一个共享的属性。字体对象通过这个 key 来区分不同的字体样式。
    display 方法接受外部状态(例如文本内容),该外部状态每次调用时可能不同,但内部状态(如字体样式)是共享的。
  2. FontFactory 类(享元工厂)
    FontFactory 类是享元模式的核心,它负责管理所有的字体实例(通过 fontPool),并确保字体的复用。
    当请求字体对象时,GetFont 方法首先检查字体池(fontPool)中是否已有对应的字体,如果存在则返回现有实例,否则创建一个新的字体并存储到池中。
    clear 方法清理享元池中的所有对象,防止内存泄漏。
  3. 客户端
    客户端通过 FontFactory 获取字体对象,并调用 display 方法来展示文本。不同的文本内容共享相同的字体对象(即内部状态)。

总结

角色 描述
Font 享元类,存储共享的字体内部状态。
FontFactory 享元工厂,管理字体实例的池并保证字体复用。
Client 客户端,使用 FontFactory 获取字体并设置外部状态。
Flyweight 模式的优点:
节省内存:多个相同对象共享相同的内部状态,只存储一份,减少内存占用。
提高性能:通过复用相同的对象来减少对象创建的开销。
减少垃圾回收负担:复用对象减少了不必要的对象创建和销毁。
Flyweight 模式的缺点:
外部状态管理复杂:需要在客户端管理外部状态,将外部状态传递给享元对象。
实现复杂度:需要设计享元池和管理对象的共享方式,代码复杂度增加。

适用场景

游戏开发:需要大量相同的图形对象,如背景、敌人等。
文本处理:需要处理大量字符、字体、样式的渲染。
图形渲染:需要共享形状、颜色等属性的图形对象。
网络请求:不同请求使用相同的网络连接池。

总结

Flyweight 模式通过对象的共享来有效减少内存消耗,适用于需要大量相似对象的场景。它通过将对象的内部状态与外部状态区分,并只共享内部状态,从而提高了系统的效率。


http://www.kler.cn/a/536941.html

相关文章:

  • go语言中的反射
  • 【蓝桥杯—单片机】第十届省赛真题代码题解题笔记 | 省赛 | 真题 | 代码题 | 刷题 | 笔记
  • 【自学笔记】Agent的重点知识点-持续更新
  • 开工了,搬砖了!
  • vue 引入百度地图和高德天气 都得获取权限
  • 司库建设-融资需求分析与计划制定
  • windows同时安装两个不同版本的Mysql
  • C++ ——— 多态的概念及其原理和实现
  • BGP边界网关协议(Border Gateway Protocol)选路、属性(一)
  • 使用 java -jar 命令启动 Spring Boot 应用时,指定特定的配置文件的几种实现方式
  • QWidget中嵌入QQuickWidget,从qml端鼠标获取经纬度点(double类型),发到c++端。把c++端的对象暴露个qml端调用
  • 第九天 动手实践ArkTS,创建简单的UI组件
  • BFS算法——广度优先搜索,探索未知的旅程(下)
  • 陶氏环面包络减速机:为工业视觉检测注入“精准动力”!
  • 【机器学习】深入探索SVM概念及其核方法
  • 3NF讲解
  • Web3D基础: GLTF文件材质和纹理扫盲
  • matlab simulink 117-电路故障分析
  • Day48_20250130【回校继续打卡】_单调栈part1_739.每日温度|496.下一个更大元素I|503.下一个更大元素II
  • SQL高级技巧:高效获取两表交集数据的三种方法(JOIN、IN、EXISTS)
  • Spring Cloud 01 - 微服务概述
  • “无痕模式”VS指纹浏览器,哪个更安全?
  • 自定义飞书Webhook机器人api接口
  • 【vscode源码】如何编译运行vscode及过程中问题解决
  • 在 Java 中使用 JDBC 连接数据库时,DriverManager 的主要作用是什么?请简要描述其工作原理。
  • Linux在x86环境下制作ARM镜像包