设计模式(C++)-结构型模式-享元模式
设计模式(C)-结构型模式-享元模式一、享元模式概述享元模式是一种结构型设计模式它通过共享技术来有效地支持大量细粒度对象的复用从而减少内存使用和提高性能。核心思想如果程序中有大量相似对象可以将它们共有的部分内部状态提取出来共享而变化的部分外部状态在运行时传入。形象比喻字母处理器的例子文档中有成千上万个字母‘a’、‘b’、‘c’等每个字母都有自己的字体大小、颜色、位置等如果为每个字母都创建完整的对象内存占用巨大享元模式创建一个共享的字母对象内部状态字符代码将字体、颜色、位置等作为外部状态输入。要解决什么问题问题背景软件系统中有大量相似对象这些对象造成了很大的内存开销大部分对象的状态可以分为内部状态和外部状态示例在游戏开发中有成千上万的树、草、士兵等对象每个对象都有位置、方向、颜色等属性。如果为买个对象都创建完整的实例内存会迅速耗尽。核心概念内部状态vs外部状态内部状态存储在享元对象内部不会随环境变化而改变可以被共享例如字符代码、树的模型、士兵的类型外部状态随环境变化而改变不能被共享由客户端保存使用时传入例如字符的位置、颜色、大小树的坐标、朝向二、享元模式UML类图场景森林模拟实现一个森林模拟系统森林中有成千上万颗树但树的类型内部状态只有几种三、代码实现//flyweight.h/* 享元模式(flyWeight pattern)是一种结构型设计模式它通过共享技术来有效地支持大量细粒度对象的复用从而减少内存使用和提高性能。 核心思想如果程序中有大量相似对象可以将它们共有的部分内部状态提取出来共享而变化的部分外部状态在运行时传入。 形象比喻字母处理器的例子 - 文档中有成千上万个字母‘a’、‘b’、‘c’等 - 每个字母都有自己的字体大小、颜色、位置等 - 如果为每个字母都创建完整的对象内存占用巨大 - 享元模式创建一个共享的字母对象内部状态字符代码将字体、颜色、位置等作为外部状态输入。 */#pragmaonce#includeiostream#includestring#includeunordered_map#includevector#includememoryusingnamespacestd;//--------------------享元类树的类型内部状态------------------------------//FlyWeightclassTreeType{private:string name_;//内部状态string color_;//内部状态string texture_;//内部状态public:TreeType(conststringname,conststringcolor,conststringtexture):name_(name),color_(color),texture_(texture){}voiddraw(intx,inty)const;stringgetKey()const;};//-------------------享元工厂------------------------------------classTreeFactory{private://存储共享的数类型对象staticunordered_mapstring,shared_ptrTreeTypetreeTypes_;public:staticshared_ptrTreeTypegetTreeType(conststringname,conststringcolor,conststringtexture);staticvoidshowTreeTypes();};//--------------------树类包含外部状态-------------------------------classTree{private:intx_;//外部状态inty_;//外部状态shared_ptrTreeTypetype_;//共享的内部状态public:Tree(intx,inty,shared_ptrTreeTypetype):x_(x),y_(y),type_(type){}voiddraw()const;};//------------------森林类(管理大量树)----------------------------------------classForest{private:vectorTreetrees_;public:voidplantTree(intx,inty,conststringname,conststringcolor,conststringtexture);voiddraw()const;};voidtestFlyWeight();//flyweight.cc#includeflyweight.h//--------------------享元类树的类型内部状态------------------------------voidTreeType::draw(intx,inty)const{cout在位置(x, y) 绘制一棵name_树颜色color_纹理texture_endl;}stringTreeType::getKey()const{returnname__color__texture_;}//-------------------享元工厂------------------------------------// 静态成员初始化unordered_mapstring,shared_ptrTreeTypeTreeFactory::treeTypes_;shared_ptrTreeTypeTreeFactory::getTreeType(conststringname,conststringcolor,conststringtexture){std::string keyname_color_texture;autoittreeTypes_.find(key);if(it!treeTypes_.end()){// 已存在返回共享对象returnit-second;}else{// 不存在创建新对象并存储autotreeTypestd::make_sharedTreeType(name,color,texture);treeTypes_[key]treeType;returntreeType;}}voidTreeFactory::showTreeTypes(){std::cout\n已存在的树类型享元对象std::endl;for(constautopair:treeTypes_){std::cout pair.firststd::endl;}std::cout总共treeTypes_.size() 种类型std::endl;}//--------------------树类包含外部状态-------------------------------voidTree::draw()const{type_-draw(x_,y_);}//------------------森林类(管理大量树)----------------------------------------voidForest::plantTree(intx,inty,conststringname,conststringcolor,conststringtexture){// 从工厂获取共享的树类型autotypeTreeFactory::getTreeType(name,color,texture);// 创建树对象包含外部状态trees_.emplace_back(x,y,type);}voidForest::draw()const{std::cout\n森林中有 trees_.size() 棵树std::endl;for(constautotree:trees_){tree.draw();}}voidtestFlyWeight(){coutFlyWeight Endendl;Forest forest;// 种植大量树但只有少数几种类型forest.plantTree(1,1,橡树,绿色,橡木纹理);forest.plantTree(2,3,松树,深绿,松木纹理);forest.plantTree(3,5,橡树,绿色,橡木纹理);// 共享橡树类型forest.plantTree(4,7,枫树,红色,枫木纹理);forest.plantTree(5,9,松树,深绿,松木纹理);// 共享松树类型forest.plantTree(6,11,橡树,绿色,橡木纹理);// 共享橡树类型forest.plantTree(7,13,橡树,黄色,秋日橡木纹理);// 新类型forest.plantTree(8,15,松树,深绿,松木纹理);// 共享松树类型// 绘制森林forest.draw();// 显示共享的树类型TreeFactory::showTreeTypes();coutFlyWeight Endendl;}四、优缺点总结优点大幅减少内存使用通过共享相似对象避免重复粗存储相同数据提高性能减少了对象的创建和销毁开销支持大量细粒度对象可以处理远超内存容量的对象数量分离内部状态和外部状态使得程序结构更清晰、职责更分明缺点增加系统复杂性需要区分内部装和外部状态增加了设计和实现的复杂度可能增加运行时间:需要从挖我不传递状态可能增加方法调用的开销线程安全问题共享的享元对象可能需要考虑线程安全问题不适用于所有场景如果对象之间差异很大享元模式反而会增加复杂度。