文档编辑器(该编辑器只能编辑24个字母和空格, 可以修改字体大小, 颜色)(位置可以动态计算).
CharacterFactory 负责管理24个字符和空格CharacterFlyweight对象. 里面需要有一个Flyweight列表, 列表存储对象指针.
Flyweight 定义了接口 Draw(Contex* pContex)
CharacterFlyweight 存储内部状态的类, 并实现Draw(Contex* pContex). 有一个m_char存储字母(它就是内部状态了).
Client 管理Contex与CharacterFactory::m_char的关系.
Client不一定只对Log操作, 它也可以对TXT, XML, SelfDefine类操作, 根据需求而定.
如图:
CharacterFactory GetCharacter(char ch) { for c in FlyweightList if c.m_char == ch return c return new CharacterFactory(ch) } CharacterFlyweight::Draw(Contex* pContex) { ... } // Client 调用 Flyweight* pFlyweight = CharacterFactory::GetCharacter('a'); // 计算'a'的Contex(算法计算也行, 存储也行) ... // 绘制 pFlyweight->Draw(pContex); //
1. 刚看书中的例子时, 就是不明白书中的文档编辑器使用Flyweight模式是怎样的省内存.
例如 "aaabbbcccddd" 这段字符, 长度是12字节, 3个a, 3个b, 3个c, 3个d, 加上12个字母的位置, 字体, 颜色信息, 12个Context对象. 如果字母a, b, c, d共享了, 就编程4 + 12个Context对象, 就省了8个字节, 但是有一个问题, 就是共享的话, 使用者需要存储字符的指针, 一个字符的指针需要的容量比一个字母要多啊! 所以总是认为为了一个字节的字母而使用Flyweight模式, 好像并没有什么好处吧(这种情况是没有什么好处的).
2. 自己弄错了, 省空间是一个方面, 而看看Flyweight模式的的意图(运用共享技术有效地支持大量细粒度的对象), 这种小粒度对象的频繁创建是很占开销的.
3. 在文档编辑器处理中 字母有内部状态, Context也有内部状态(例如Context中的字体也是有限的, 颜色也是有限的), 这可以作为Context的内部状态, 从而再分出了另一维度的Flyweight, 此时内存空间就真的大大减少了.
如图:(这里用了两个Flyweight模式)
图2
4. 另外, Flyweight的意图与pool思想的意图是相同的都是为了避免对象的频繁创建销毁.
但是有区别的, Flyweight的内部状态是一致的公共的. 而pool中的对象的状态是各管各的, 用户使用pool中的对象是可以改变对象中的状态的. 而Flyweight则不行, 因为Flyweight是共享的. 更细致的说就是FlyweightFactory的实现可以使用pool的思想来管理Flyweight对象. Flyweight处理使用了pool思想外, 还强调要把内部状态和外部状态分离, 而且内部状态和外部状态存在映射关系, 这种关系需要一定的策略来管理(动态计算或者静态存储? 都可以, 看需求).