设为首页 加入收藏

TOP

[学习笔记]设计模式之Flyweight(一)
2019-09-03 03:21:42 】 浏览:44
Tags:学习 笔记 设计模式 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

 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主要针对存储节约提出了解决方案,它所节约的部分主要由以下几个因素决定:

  1. 因为共享,实例总数减少的数目
  2. 对象内部状态的平均数目
  3. 外部状态是计算的还是存储的

共享的Flyweight越多,存储节约也就越多。节约量随着共享状态的增多

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇【.NET】单例模式标准写法 下一篇[Design Pattern] Substitute Int..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目