动机
在软件系统采用纯粹对象方案的问题在于大量细粒度的对象会很快充斥在系统中,从而带来很高的运行时代价——主要指内存需求方面的代价。
如何在避免大量细粒度对象问题的同时,让外部客户程序仍然能够透明地适用面向对象的方式来进行操作?
代码举例:创建字体对象时,同一个key的字体对象只有一个,而不是有很多
// 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++ 代码例子,演示了享元模式的实现。这个例子以文本编辑器中的字符格式化为场景。
#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采用对象共享的做法来降低系统中对象的个数,从而降 低细粒度对象给系统带来的内存压力。在具体实现方面,要注意对 象状态的处理。
对象的数量太大从而导致对象内存开销加大——什么样的数量才 算大?这需要我们仔细的根据具体应用情况进行评估,而不能凭空 臆断。