享元模式是一种结构型设计模式,它通过共享对象来最小化内存使用或计算开销。这种模式适用于大量相似对象的情况,通过共享这些对象的公共部分来减少资源消耗。
基本概念
享元模式的核心思想是将对象的内在状态 (不变的部分)和外在状态(变化的部分)分离:
-
内在状态(Intrinsic State):存储在享元内部,可以被多个对象共享,独立于具体场景
-
外在状态(Extrinsic State):取决于具体场景,通常由客户端保存或计算
实现示例
下面是一个简单的享元模式实现示例,模拟文本编辑器中的字符处理:
#include <iostream>
#include <string>
#include <unordered_map>
// 享元类 - 表示字符及其内在状态(字体、大小等)
class Character {
public:
Character(char symbol, const std::string& font, int size)
: symbol_(symbol), font_(font), size_(size) {}
void display(int position) const {
std::cout << "Character: " << symbol_
<< ", Font: " << font_
<< ", Size: " << size_
<< ", Position: " << position << std::endl;
}
private:
char symbol_;
std::string font_;
int size_;
};
// 享元工厂 - 创建和管理享元对象
class CharacterFactory {
public:
Character* getCharacter(char key, const std::string& font, int size) {
// 使用组合键来唯一标识享元对象
std::string compositeKey = std::string(1, key) + "_" + font + "_" + std::to_string(size);
if (characters_.find(compositeKey) == characters_.end()) {
characters_[compositeKey] = new Character(key, font, size);
}
return characters_[compositeKey];
}
~CharacterFactory() {
for (auto& pair : characters_) {
delete pair.second;
}
}
private:
std::unordered_map<std::string, Character*> characters_;
};
// 客户端代码
int main() {
CharacterFactory factory;
// 文档中的字符及其位置
std::string text = "Hello, World!";
for (size_t i = 0; i < text.size(); ++i) {
char c = text[i];
// 假设所有字符使用相同的字体和大小
Character* character = factory.getCharacter(c, "Arial", 12);
character->display(i); // 位置是外在状态,由客户端提供
}
return 0;
}
UML结构
享元模式的组成
-
Flyweight(享元接口):定义享元对象的接口
-
ConcreteFlyweight(具体享元):实现享元接口,存储内在状态
-
UnsharedConcreteFlyweight(非共享具体享元):不需要共享的享元实现
-
FlyweightFactory(享元工厂):创建和管理享元对象,确保合理共享
-
Client(客户端):维护对享元的引用,计算或存储外在状态
适用场景
-
一个应用程序使用了大量相似对象
-
由于大量对象造成很大的存储开销
-
对象的大多数状态可以变为外部状态
-
移除了外部状态后,可以用较少的共享对象替代大量对象
优点
-
减少内存使用,因为共享了相似对象
-
减少了对象的创建数量,提高了性能
-
将状态外部化,使得对象更轻量
缺点
-
增加了系统复杂性,需要分离内在和外在状态
-
可能需要线程安全考虑,因为享元对象是共享的
-
外在状态需要由客户端维护和管理
实际应用
-
文本编辑器中的字符处理
-
游戏开发中的粒子系统
-
图形编辑器中的图形对象
-
数据库连接池
-
任何需要大量细粒度对象的场景
在C++标准库中,std::string
的写时复制(COW)实现某种程度上也使用了享元模式的思想。