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

相关推荐
依瑾雅22 分钟前
Scala语言的人工智能
开发语言·后端·golang
大雄野比26 分钟前
Kotlin 循环与函数详解:高效编程指南
开发语言·python·kotlin
NoneCoder28 分钟前
JavaScript系列(64)--响应式状态管理实现详解
开发语言·javascript·ecmascript
yzpyzp32 分钟前
拆解Kotlin中的by lazy:从语法糖到底层实现
android·开发语言·kotlin
IManiy32 分钟前
Java/Kotlin双语革命性ORM框架Jimmer(一)——介绍与简单使用
java·开发语言·kotlin
I'mAlex44 分钟前
【Python】一文讲透Pygame教程,非常详细
开发语言·python·pygame
晨小白先森1 小时前
matlab中中文注释出现乱码问题(部分解决)
开发语言·matlab
轩辰q1 小时前
正则表达式(python版最全面,最易懂)
开发语言·python
web182854825121 小时前
[入门一]C# webApi创建、与发布、部署、api调用
开发语言·c#
island13141 小时前
【QT】: 初识 QWidget 控件 | QWidget 核心属性(API) | qrc 文件
开发语言·qt