定义
Flyweight 模式(享元模式) 是一种结构型设计模式,它旨在通过共享对象来有效支持大量细粒度对象的复用。该模式主要通过共享细节来减少内存使用,提升性能,尤其在需要大量对象时非常有效。
基本思想:
共享对象:对象的内存资源和属性可以共享,从而减少内存消耗。
外部状态与内部状态的区分:
内部状态:在对象内部存储的属性,通常是共享的,可以被多个对象共享。
外部状态:依赖于具体的上下文环境,通常是每个对象独有的,不共享的。
Flyweight 模式的核心目标是:
- 共享和复用:将对象的共享部分提取出来,以减少内存消耗。
- 提高性能:通过避免重复创建大量相似的对象来提升性能。
Flyweight 模式适用于以下场景:
需要大量对象:当一个系统需要大量相似对象时,使用 Flyweight 模式可以减少内存开销。
对象细粒度重复:系统中很多对象的内部状态相同,只有外部状态不同。
资源有限的环境:在内存或者其他资源有限的环境中,Flyweight 可以有效降低资源消耗。
对象状态可分离:可以将对象的状态分为内部状态和外部状态。
常见应用场景:
图形应用:多个相同的图形(如圆形、矩形)共享相同的属性。
文字渲染:不同的字符共享相同的字体样式、大小等。
类设计
Flyweight 模式通常包括以下角色:
Flyweight(享元):定义共享对象的接口。
ConcreteFlyweight(具体享元):实现 Flyweight 接口,存储共享的状态。
FlyweightFactory(享元工厂):管理享元对象池,确保对象复用。
Client(客户端):使用享元对象,区分内部和外部状态。
#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;
}
运行代码后输出如下:
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,则是新创建的字体对象。
类设计分析
- Font 类(享元类)
Font 类代表一个字体对象,它有一个 key 属性作为内部状态。这是一个共享的属性。字体对象通过这个 key 来区分不同的字体样式。
display 方法接受外部状态(例如文本内容),该外部状态每次调用时可能不同,但内部状态(如字体样式)是共享的。 - FontFactory 类(享元工厂)
FontFactory 类是享元模式的核心,它负责管理所有的字体实例(通过 fontPool),并确保字体的复用。
当请求字体对象时,GetFont 方法首先检查字体池(fontPool)中是否已有对应的字体,如果存在则返回现有实例,否则创建一个新的字体并存储到池中。
clear 方法清理享元池中的所有对象,防止内存泄漏。 - 客户端
客户端通过 FontFactory 获取字体对象,并调用 display 方法来展示文本。不同的文本内容共享相同的字体对象(即内部状态)。
总结
角色 描述
Font 享元类,存储共享的字体内部状态。
FontFactory 享元工厂,管理字体实例的池并保证字体复用。
Client 客户端,使用 FontFactory 获取字体并设置外部状态。
Flyweight 模式的优点:
节省内存:多个相同对象共享相同的内部状态,只存储一份,减少内存占用。
提高性能:通过复用相同的对象来减少对象创建的开销。
减少垃圾回收负担:复用对象减少了不必要的对象创建和销毁。
Flyweight 模式的缺点:
外部状态管理复杂:需要在客户端管理外部状态,将外部状态传递给享元对象。
实现复杂度:需要设计享元池和管理对象的共享方式,代码复杂度增加。
适用场景
游戏开发:需要大量相同的图形对象,如背景、敌人等。
文本处理:需要处理大量字符、字体、样式的渲染。
图形渲染:需要共享形状、颜色等属性的图形对象。
网络请求:不同请求使用相同的网络连接池。
总结
Flyweight 模式通过对象的共享来有效减少内存消耗,适用于需要大量相似对象的场景。它通过将对象的内部状态与外部状态区分,并只共享内部状态,从而提高了系统的效率。