C++设计模式之享元模式

动机

在软件系统采用纯粹对象方案的问题在于大量细粒度的对象会很快充斥在系统中,从而带来很高的运行时代价------主要指内存需求方面的代价。

如何在避免大量细粒度对象问题的同时,让外部客户程序仍然能够透明地适用面向对象的方式来进行操作?

代码举例:创建字体对象时,同一个key的字体对象只有一个,而不是有很多

cpp 复制代码
// Font.h
#pragma once

#include <string>

class Font {
private:
    // 字体对象的唯一标识符
    std::string key;

    // 字体对象的状态(可能包含其他属性,这里未具体展示)
    // ....

public:
    // 构造函数,根据传入的 key 初始化字体对象
    Font(const std::string& key) : key(key) {
        // ...
    }
};

// FontFactory.h
#pragma once

#include <map>
#include "Font.h"

class FontFactory {
private:
    // 存储已创建的字体对象,key 是字体对象的唯一标识符,value 是对应的字体对象指针
    std::map<std::string, Font*> fontPool;

public:
    // 获取字体对象
    Font* GetFont(const std::string& key);

    // 清理字体池
    void clear();
};

// FontFactory.cpp
#include "FontFactory.h"

Font* FontFactory::GetFont(const std::string& key) {
    // 查找字体池中是否已存在对应 key 的字体对象
    auto iter = fontPool.find(key);

    if (iter != fontPool.end()) {
        // 如果存在,则直接返回已有的字体对象
        return iter->second;
    } else {
        // 如果不存在,则创建新的字体对象,并加入字体池
        Font* font = new Font(key);
        fontPool[key] = font;
        return font;
    }
}

void FontFactory::clear() {
    // 清理字体池中的所有字体对象
    for (auto& entry : fontPool) {
        delete entry.second;
    }
    fontPool.clear();
}

享元模式定义

运用共享技术有效地支持大量细粒度的对象。

chatGPT给出的享元模式的例子

以下是一个简单的 C++ 代码例子,演示了享元模式的实现。这个例子以文本编辑器中的字符格式化为场景。

cpp 复制代码
#include <iostream>
#include <map>

// 享元接口
class CharacterFormat {
public:
    virtual void applyFormat() const = 0;
};

// 具体享元类,代表不同的字符格式
class BoldFormat : public CharacterFormat {
public:
    void applyFormat() const override {
        std::cout << "Applying Bold Format\n";
    }
};

class ItalicFormat : public CharacterFormat {
public:
    void applyFormat() const override {
        std::cout << "Applying Italic Format\n";
    }
};

class UnderlineFormat : public CharacterFormat {
public:
    void applyFormat() const override {
        std::cout << "Applying Underline Format\n";
    }
};

// 享元工厂类,负责创建和管理享元对象
class CharacterFormatFactory {
private:
    std::map<std::string, CharacterFormat*> formatPool;

public:
    CharacterFormat* getFormat(const std::string& key) {
        auto iter = formatPool.find(key);

        if (iter != formatPool.end()) {
            return iter->second;
        } else {
            CharacterFormat* format = nullptr;

            if (key == "Bold") {
                format = new BoldFormat();
            } else if (key == "Italic") {
                format = new ItalicFormat();
            } else if (key == "Underline") {
                format = new UnderlineFormat();
            }

            formatPool[key] = format;
            return format;
        }
    }
};

// 客户端代码
int main() {
    CharacterFormatFactory formatFactory;

    // 用户输入的字符格式
    std::string userInput = "Bold";

    // 通过享元工厂获取或创建字符格式对象
    CharacterFormat* format = formatFactory.getFormat(userInput);

    // 应用字符格式
    if (format) {
        format->applyFormat();
    } else {
        std::cout << "Invalid Format\n";
    }

    // 释放资源
    delete format;

    return 0;
}

在这个例子中,CharacterFormat 是享元接口,而 BoldFormat、ItalicFormat、UnderlineFormat 是具体的享元类。CharacterFormatFactory 是享元工厂类,负责创建和管理享元对象。在客户端代码中,用户输入字符格式的信息,通过享元工厂获取或创建相应的字符格式对象,并应用该格式。这样,相同的字符格式对象可以被多次共享,减少了资源的重复创建。

要点总结

面向对象很好地解决了抽象性的问题,但是作为一个运行在机器 中的程序实体,我们需要考虑对象的代价问题。Flyweight主要解 决面向对象的代价问题,一般不触及面向对象的抽象性问题。

Flyweight采用对象共享的做法来降低系统中对象的个数,从而降 低细粒度对象给系统带来的内存压力。在具体实现方面,要注意对 象状态的处理。

对象的数量太大从而导致对象内存开销加大------什么样的数量才 算大?这需要我们仔细的根据具体应用情况进行评估,而不能凭空 臆断。

相关推荐
数据智能老司机2 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机3 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机3 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机3 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
使一颗心免于哀伤4 小时前
《设计模式之禅》笔记摘录 - 21.状态模式
笔记·设计模式
感哥4 小时前
C++ STL 常用算法
c++
saltymilk14 小时前
C++ 模板参数推导问题小记(模板类的模板构造函数)
c++·模板元编程
感哥15 小时前
C++ lambda 匿名函数
c++
沐怡旸20 小时前
【底层机制】std::unique_ptr 解决的痛点?是什么?如何实现?怎么正确使用?
c++·面试
感哥21 小时前
C++ 内存管理
c++