11.享元模式 (Flyweight)

定义

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

基本思想:

共享对象:对象的内存资源和属性可以共享,从而减少内存消耗。

外部状态与内部状态的区分:

内部状态:在对象内部存储的属性,通常是共享的,可以被多个对象共享。

外部状态:依赖于具体的上下文环境,通常是每个对象独有的,不共享的。

Flyweight 模式的核心目标是:

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

Flyweight 模式适用于以下场景:

需要大量对象:当一个系统需要大量相似对象时,使用 Flyweight 模式可以减少内存开销。

对象细粒度重复:系统中很多对象的内部状态相同,只有外部状态不同。

资源有限的环境:在内存或者其他资源有限的环境中,Flyweight 可以有效降低资源消耗。

对象状态可分离:可以将对象的状态分为内部状态和外部状态。

常见应用场景:

图形应用:多个相同的图形(如圆形、矩形)共享相同的属性。

文字渲染:不同的字符共享相同的字体样式、大小等。

类设计

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

Flyweight(享元):定义共享对象的接口。

ConcreteFlyweight(具体享元):实现 Flyweight 接口,存储共享的状态。

FlyweightFactory(享元工厂):管理享元对象池,确保对象复用。

Client(客户端):使用享元对象,区分内部和外部状态。

cpp 复制代码
#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;
}

运行代码后输出如下:

cpp 复制代码
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 模式通过对象的共享来有效减少内存消耗,适用于需要大量相似对象的场景。它通过将对象的内部状态与外部状态区分,并只共享内部状态,从而提高了系统的效率。

相关推荐
眼镜哥(with glasses)43 分钟前
Java程序题案例分析
java·开发语言
夏子曦5 小时前
C#——NET Core 中实现汉字转拼音
开发语言·c#
꧁坚持很酷꧂6 小时前
Qt天气预报系统绘制温度曲线
开发语言·qt
电商数据girl6 小时前
【Python爬虫电商数据采集+数据分析】采集电商平台数据信息,并做可视化演示
java·开发语言·数据库·爬虫·python·数据分析
海尔辛6 小时前
学习黑客Bash 脚本
开发语言·学习·bash
小白学大数据7 小时前
分布式爬虫去重:Python + Redis实现高效URL去重
开发语言·分布式·爬虫·python
可可乐不加冰7 小时前
QT生成保存 Excel 文件的默认路径,导出的文件后缀自动加(1)(2)等等
开发语言·qt
火龙谷7 小时前
【爬虫】码上爬第6题-倚天剑
开发语言·javascript·爬虫
jk_1018 小时前
MATLAB中去除噪声
开发语言·计算机视觉·matlab
田辛 | 田豆芽8 小时前
【Python】通过`Editable Install`模式详解,解决Python开发总是import出错的问题
开发语言·python·包管理