[学习笔记]设计模式之Flyweight

为方便读者,本文已添加至索引:html

写在前面

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继承自Drinkspa

 1 class Drink {
 2 public:
 3     virtual ~Drink();    
 4     
 5     virtual void mix(Ingredient&); // can mix with other ingredients.
 6     virtual void drink();
 7  protected:
 8     Drink();
 9 }
10 
11 class BaseWine : public Drink {
12 public:
13     BaseWine(int type);
14     
15     virtual void mix(Ingredient&);
16 private:
17     int _type;
18 }

当小霍比特人拜访酿酒工坊WineFactory,想要来一杯的时候。工坊会先看看他要的这种基酒是否已经酿造好了,有的话,直接取一份给他;不然,就酿造出取之不尽的这种基酒。设计

 1 #define WHISKEY 1
 2 #define RUM     2
 3 #define VODKA   3
 4 #define GIN     4
 5 // ... other definitions ...
 6 #define MAX_TYPES 10
 7 
 8 class WineFactory {
 9 public:
10     WineFactory();
11     virtual ~WineFactory();
12     virtual BaseWine* createWine(int type);
13     
14 private:
15     BaseWine* _wine[MAX_TYPES];
16 }
17 
18 // Initialize the wine pool.
19 WineFactory::WineFactory() {
20     for (int i = 0; i < MAX_TYPES; ++i) {
21         _wine[i] = 0;
22     }
23 }
24 
25 // Check the pool before create wine.
26 BaseWine* WineFactory::createWine(int type) {
27     if (!_wine[type]) {
28         _wine[type] = new BaseWine(type);
29     }
30     
31     return _wine[type];
32 }

咱们看到,_wine数组包含一些指针,指向以基酒品种为索引的BaseWine,createWine方法首先会查找这个数组,若是数组中存在这种BaseWine,则能够直接返回,不然,new一个新的。这样作的一个好处是,酒的种类每每是有限且很少的,可是咱们能够却能够共享这些种类并用在不少不少场景下。这个简单例子的UML图以下:指针

特色总结

Flyweight主要针对存储节约提出了解决方案,它所节约的部分主要由如下几个因素决定:code

  1. 由于共享,实例总数减小的数目
  2. 对象内部状态的平均数目
  3. 外部状态是计算的仍是存储的

共享的Flyweight越多,存储节约也就越多。节约量随着共享状态的增多而增大。当对象使用大量的内部及外部状态,而且外部状态是计算出来的而非存储的时候,节约量将达到最大。因此,能够用两种方法来节约存储:用共享减小内部状态的消耗,用计算时间换取对外部状态的存储。htm

在使用Flyweight模式时,咱们须要注意如下几点:对象

  1. 删除外部状态。该模式的可用性在很大程度上取决因而否容易识别外部状态并将它从共享对象中删除。
  2. 管理共享对象。由于对象是共享的,用户不能直接对它进行实例化,所以FlyweightFactory能够帮助用户查找某个特定的Flyweight对象。

写在最后

今天的笔记就到这里了,欢迎你们批评指正!若是以为能够的话,好文推荐一下,我会很是感谢的!

相关文章
相关标签/搜索