[学习笔记]设计模式之Flyweight
为方便读者,本文已添加至索引:
设计模式
学习笔记索引
写在前面
Flyweight(享元)模式运用共享技术,可以有效地支持大量细粒度的对象。今天我们会去参观小霍比特人们的酿酒工坊……等等,不是享元模式吗?那好吧,我们推迟到示例一节中前往参观。
我们在做面向对象的设计时,常常希望能用对象来表示某个具体的事物,比如一个红富士苹果或是一辆凯迪拉克跑车。当我们把这种思维带到一些程序设计任务中去时,可能就会遭遇到处理存储开销和程序本身灵活性的一个平衡问题。例如,我们在设计一个游戏,主人公走到一片苹果园,看见满满一屏幕的苹果。更高级的是,每个苹果都能与主人公进行交互,不论是近处的(直接摘下来吃了),还是远处的(我拿石头扔,我扔)。如果每个苹果都用不同的对象来表示,的确可以极大提高这个游戏的视觉效果,因为这样我们可以给每个苹果定制完全不同的外观(这属于苹果的内部因素),俗话说,世界上没有两个相同的苹果(...叶子如是,苹果应该也不差)。但是,如果苹果园里,有成千上万个苹果呢?这能造成巨大的存储开销。事实上,玩游戏的时候,我们才不会太在意这个苹果长什么样,除非是两个完全不同种类的苹果。所以,如果能只存储一个苹果的外观,渲染成千上万个(存在距离、光照等外部因素),我们也不会觉得哪里不舒服,还是会朝那些苹果扔石头。这只被共享的苹果,便引出了我们的Flyweight模式。先来看看它的一些要点。
要点梳理
- 目的分类
- 对象结构型模式
- 范围准则
- 对象(该模式处理对象间的关系,这些关系在运行时刻是可以变化的,更具动态性)
- 主要功能
- 运用共享技术有效地支持大量细粒度的对象
- 适用情况
- 当以下情况都成立时,使用Flyweight模式:
- 一个应用程序使用了大量的对象
- 完全由于使用大量的对象,造成很大的存储开销
- 对象的大多数状态都可变为外部状态
- 如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象
- 应用程序不依赖于对象标识
- 参与部分
- Flyweight:描述一个接口,通过这个接口flyweight可以接受并作用于外部状态
- ConcreteFlyweight:实现上述接口,并为内部状态(如果有的话)增加存储空间。ConcreteFlyweight对象必须是可共享的。它所存储的状态必须是内部的,它必须独立于对象的场景
- UnsharedConcreteFlyweight:并非所有的Flyweight子类都需要被共享,UnsharedConcreteFlyweight通常将ConcreteFlyweight作为子节点
- FlyweightFactory:创建并管理Flyweight对象,并且确保合理地共享Flyweight对象。
- Client:维持一个对flyweight的引用,计算或存储一个(多个)flyweight的外部状态。
- 协作过程
- flyweight执行时所需的状态必定是内部的或外部的。内部状态存储于ConcreteFlyweight对象之中;而外部对象则由Client对象存储或计算。当用户调用flyweight对象的操作时,将该状态传递给它。
- 用户不应直接对ConcreteFlyweight类进行实例化,而只能从FlyweightFactory对象得到ConcreteFlyweight对象,这可以保证对它们适当地进行共享。
- UML图
示例分析 - 霍比特人的酿酒工坊
还记得这群可爱的小霍比特人们吗?(如果不了解他们,请见AbstractFactory笔记)喜欢开Party的他们,自然得有喝不完的好酒相伴。时の魔导士早先的时候曾给他们留下过一座酿酒工坊WineFactory。这座工坊可以生产各式基酒,譬如威士忌Whiskey,朗姆酒Rum,葡萄酒Wine等等。小霍比特人们,可以根据自己的口味,添加一些果汁、牛奶、咖啡、糖等等辅料,从而调制出一杯美味的鸡尾酒Cocktail。
在这个魔法世界里,我们忽略每种基酒的酿造工艺上的不同,而假定它们的内部状态是相同的(都含有酒精),区分它们的仅仅是口感的类型不同。因此,我们可以将基酒视作为一类ConcreteFlyweight,基酒类BaseWine继承自Drink:
class Drink {
public:
virtual ~Drink(); virtual void mix(Ingredient&); // can mix with other ingredients.
virtual void drink();
protected:
Drink();
} class BaseWine : public Drink {
public:
BaseWine(int type); virtual void mix(Ingredient&);
private:
int _type;
}
当小霍比特人拜访酿酒工坊WineFactory,想要来一杯的时候。工坊会先看看他要的这种基酒是否已经酿造好了,有的话,直接取一份给他;否则,就酿造出取之不尽的这种基酒。
#define WHISKEY 1
#define RUM 2
#define VODKA 3
#define GIN 4
// ... other definitions ...
#define MAX_TYPES 10 class WineFactory {
public:
WineFactory();
virtual ~WineFactory();
virtual BaseWine* createWine(int type); private:
BaseWine* _wine[MAX_TYPES];
} // Initialize the wine pool.
WineFactory::WineFactory() {
for (int i = ; i < MAX_TYPES; ++i) {
_wine[i] = ;
}
} // Check the pool before create wine.
BaseWine* WineFactory::createWine(int type) {
if (!_wine[type]) {
_wine[type] = new BaseWine(type);
} return _wine[type];
}
我们看到,_wine数组包含一些指针,指向以基酒品种为索引的BaseWine,createWine方法首先会查找这个数组,如果数组中存在这种BaseWine,则可以直接返回,否则,new一个新的。这样做的一个好处是,酒的种类往往是有限且不多的,但是我们可以却可以共享这些种类并用在很多很多场景下。这个简单例子的UML图如下:
特点总结
Flyweight主要针对存储节约提出了解决方案,它所节约的部分主要由以下几个因素决定:
- 因为共享,实例总数减少的数目
- 对象内部状态的平均数目
- 外部状态是计算的还是存储的
共享的Flyweight越多,存储节约也就越多。节约量随着共享状态的增多而增大。当对象使用大量的内部及外部状态,并且外部状态是计算出来的而非存储的时候,节约量将达到最大。所以,可以用两种方法来节约存储:用共享减少内部状态的消耗,用计算时间换取对外部状态的存储。
在使用Flyweight模式时,我们需要注意以下几点:
- 删除外部状态。该模式的可用性在很大程度上取决于是否容易识别外部状态并将它从共享对象中删除。
- 管理共享对象。因为对象是共享的,用户不能直接对它进行实例化,因此FlyweightFactory可以帮助用户查找某个特定的Flyweight对象。
写在最后
今天的笔记就到这里了,欢迎大家批评指正!如果觉得可以的话,好文推荐一下,我会非常感谢的!
[学习笔记]设计模式之Flyweight的更多相关文章
- [学习笔记]设计模式之Abstract Factory
写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 在上篇笔记Builder设计模式中,时の魔导士祭出了自己的WorldCreator.尽管它因此能创造出一个有山有树有房子的世界,但是白 ...
- [学习笔记]设计模式之Builder
写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 作为一个新入职的魔导士呢,哦不,是程序员,我以为并没有太多机会去设计项目的软件架构.但是,工作一段时间之后,自己渐渐意识到,哪怕是自己 ...
- [学习笔记]设计模式之Adapter
写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 Adapter(适配器)模式主要解决接口不匹配的问题.为此,让我们要回到最初Builder模式创建平行世界时,白雪公主和小霍比特人的谜 ...
- [学习笔记]设计模式之Bridge
写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 “魔镜啊魔镜,谁是这个世界上最美丽的人?”月光中,一个低沉的声音回荡在女王的卧室.“是美丽的白雪公主,她正和小霍比特人们幸福快乐地生活 ...
- [学习笔记]设计模式之Prototype
写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 在笔记Builder模式中,我们曾见到了最初用于创建平行世界的函数createWorld,并且它是Mage类的成员函数(毕竟是专属于魔 ...
- [学习笔记]设计模式之Command
为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 在上篇Chain of Responsibility(职责链)模式笔记中,我们学习了一种行为型设计模式.今天,我们继续这一主题,来学习 ...
- [学习笔记]设计模式之Chain of Responsibility
为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 最近时间比较紧,所以发文的速度相对较慢了.但是看到园子里有很多朋友对设计模式感兴趣,我感觉很高兴,能够和大家一起学习这些知识. 之前的 ...
- [学习笔记]设计模式之Composite
为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 在Composite(组合)模式中,用户可以使用多个简单的组件以形成较大的组件,而这些组件还可能进一步组合成更大的.它重要的特性是能够 ...
- [学习笔记]设计模式之Proxy
为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 “魔镜啊魔镜,谁是这个世界上最美丽的人?” 每到晚上,女王都会问魔镜相同的问题(见Decorator模式).这是她还曾身为女巫时留下的 ...
随机推荐
- (转载)VC/MFC 工具栏上动态添加组合框等控件的方法
引言 工具条作为大多数标准的Windows应用程序的 一个重要组成部分,使其成为促进人机界面友好的一个重要工具.通过工具条极大方便了用户对程序的操作,但是在由Microsoft Visual C++开 ...
- JavaScript高级程序设计45.pdf
客户区坐标位置 clientX和clientY保存着鼠标指针在视口中的水平位置坐标和垂直位置坐标(显示出页面的部分叫做客户区,坐标信息不包括页面的滚动距离) var div=document.getE ...
- fork和缓冲区
fork在面试中经常被问到,在这里复习一下. frok创建子进程,父子进程共享.text段,子进程获得父进程数据段.堆和栈的副本,由于在fork之后经常跟随者exec,所以很多实现并不执行父进程数据段 ...
- Test2014-3-1 魅力值比较
魅力值比较 [问题描述] 大学生恋爱的问题造成了数量众多的异地恋,有许多J大的女生早早被Q大男生追走,这导致了J大男生的强烈不满.就在吐血高调地向一位J大美女展开攻势的之后,J大男生终于爆发了. 为了 ...
- Tornado自定义分布式session框架
一.session框架处理请求执行的流程: 1.服务器端生成随机的cookie字符串 2.浏览器发送请求,服务器将cookie返回给浏览器. 3.服务器在生成一个字典.字典的key为cookie,va ...
- fzu 2135 数字游戏 【水题】
Problem 2135 数字游戏 Accept: 253 Submit: 392Time Limit: 1000 mSec Memory Limit : 32768 KB Problem ...
- (转)Spring整合Redis作为缓存
采用Redis作为Web系统的缓存.用Spring的Cache整合Redis. 一.关于redis的相关xml文件的写法 <?xml version="1.0" ...
- Servlet文件上传(ServletFIleUpload,DiskFileItemFactory,FileItem)
1:我们学的是表单文件上传,就是在一个FORM中提交相应的信息,和之前我们的提交的注册信息之类的表单是不同的,所以要先改变一下FORM的属性,enctype="multipart/form- ...
- spring beans源码解读之--XmlBeanFactory
导读: XmlBeanFactory继承自DefaultListableBeanFactory,扩展了从xml文档中读取bean definition的能力.从本质上讲,XmlBeanFactory等 ...
- JavaScript 应用开发 #1:理解模型与集合
在 < Backbone 应用实例 > 这个课程里面,我们会一起用 JavaScript 做一个小应用,它可以管理任务列表,应用可以创建新任务,编辑还有删除任务等等.这个实例非常好的演示了 ...