Flyweight 享元模式 --对象结构型模式

1、意图

        运用共享技术有效地支持大量细粒度的对象。


2、动机

         有些应用程序得益于在整个设计过程中采用对象技术,但简单化的实现代价极大。

         例如,大多数文档编辑器的实现都有文本格式化和编辑功能,这些功能在一定程度上是模块化的。面向对象的文档编辑器通常使用对象来表示嵌入的成分,例如表格和图形。尽管用对象来表示文档中的每个字符会极大地提高应用程序的灵活性,但是这些编辑器通常并不这样做。字符和嵌入成分可以在绘制和格式化时统一处理,从而在不影响其他功能的情况下能对应用程序进行扩展,支持新的字符集。应用程序的对象结构可以模拟文档的物理结构。下图显示了一个王丹编辑器怎样使用对象来表示字符。


        但这种设计的确定在于代价太大。即使是一个中等大小的文档也可能要求成百上千的字符对象,这会耗费大量内存,产生难以接受的运行开销。所以通常并不是对每个字符都用一个对象来表示的。Flyweight模式描述了如何共享对象,使得可以细粒度地使用它们而无需高昂的代价。

        flyweight是一个共享对象,它可以同时在多个场景(context)中使用,并且在每个场景中flyweight都可以作为一个独立的对象--这一点与非共享对象的实例没有区别。flyweight不能对它所运行的场景作出任何假设,这里的关键概念是内部状态和外部状态之间的区别。内部状态存储于fleweight中,它包含了独立于flyweight场景的信息,这些信息使得flyweight可以被共享,而外部状态取决于Flyweight场景,并根据场景而变化,因此不可共享。用户对象负责在必要的时候将外部状态传递给Flyweight。

        Flyweight模式对那些通常因为数量太大而难以用对象来表示的概念或实体进行建模。例如,文档编辑器可以为字母表中的每一个字母创建一个flyweight。每个flyweight存储一个字符代码,但它在文档中的位置和排版风格可以在字符出现时由正文排版算法和使用的格式化命令决定。字符代码是内部状态,而其他的信息则是外部状态。

        逻辑上,文档中的给定字符每次出现都有一个对象与其对应,如下图所示。


        然而,物理上的每个字符共享一个flyweight对象,而这个对象出现在文档结构中的不同地方。一个特定字符对象的每次出现都指向同一个实例,这个实例位于flyweight对象的共享池中。 这些对象的类结构如下图所示。



        Glyph是图形对象的抽象类,其中有些对象可能是flyweight。基于外部装填的哪些操作将外部状态作为参量传递给它们。例如,Draw和Intersects在执行之前,必须知道glyph所在的场景,如下页上图所示。

        表示字母“a”的fleyweight只存储相应的字符代码;它不需要存储字符的位置或字体。用户提供与场景相关的信息,根据此信息flyweight绘出它自己。例如Row glyph知道它的子女应该在哪儿绘制自己才能保证它们是横向排列的。因此Row glyph可以在绘制请求中向每一个子女传递它的位置。



        由于不同的字符对象数远小于文档中的字符数,因此,对象的总数远小于一个初次执行的程序所使用的对象数目。对于一个所有字符都使用同样的字体和颜色的文档而言,不管这个文档有多长,需要分配100个左右的字符对象(大约是ASCII字符集的数目)。由于大多数文档使用的字体颜色组合不超过10种,实际应用中这一数目不会明显增加。因此,对单个字符进行对象抽象是具有实际意义的。

        3、适用性

        Flyweight模式的有效性很大程度上取决于如何使用它以及在何处使用它。挡一下情况都成立时使用Flyweight模式:

        · 一个应用程序使用了大量的对象。

        · 完全由于使用大量的对象,造成很大的存储开销。

        · 对象的大多数状态都可变为外部状态。

        · 如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。

        · 应用程序不依赖于对象标识。由于Flyweight对象可以被共享,对于概念上明显有别的对象,标识测试将返回真值。


4、结构

        下面的对象图说明了如何共享flyweight。


5、参与者

        · Flyweight(Glyph)

          -- 描述一个接口,通过这个接口flyweight可以接受并作用于外部状态。

        · ConcreteFlyweight(Character)

          -- 实现Flyweight接口,并未内部状态(如果有的话)增加存储空间。

                concreteFlyweight对象必须是可共享的。它所存储的状态必须是内部的;即,它必须独立于ConcreteFlyweight对象的场景。

        · UnsharedConcreteFlyweight(Row,Column)

          -- 并非所有的Flyweight子类都需要被共享。Flyweight接口使共享成为可能,但它并不强制共享。在Flyweight对象结构的某些层次,UnsharedConcreteFlyweight对象通常将ConcreteFlyweight对象作为子节点(Row和Column就是这样)。

        · FlyweightFactory

          -- 创建管理flyweight对象。

          --确保合理地共享flyweight。当用户请求一个flyweight时,FlyweightFactory对象提供一个已创建的实例或者创建一个(如果不存在的话)。

        ·Client

          -- 维持一个对flyweight的引用。

          -- 计算或存储一个(多个)flyweight的外部状态。


6、协作

        · flyweight执行时所需的状态必定是内部的或外部的。内部状态存储于ConcreteFlyweight对象之中;而外部对象则由Client对象存储或计算。当用户调用flyweight对象的操作时,将该状态传递给它。

        · 用户不应直接对ConcreteFlyweight类进行实例化,而只能从FlyweightFacory对象得到ConcreteFlyweight对象,这可以保证对它们适当地进行共享。


7、效果

        使用Flyweight模式时,传输、查找和/或计算外部状态都会产生运行时的开销,尤其当flyweight原先被存储为内部状态时。然而,空间上的节省抵消了这些开销。共享的flyweight越多,空间节省也就越大。

        存储节约由以下几个因素决定:

        · 因为共享,实例总数减少的数目;

        · 对象内部状态的平均数目;

        · 内部状态是计算的还是存储的。


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

        Flyweight模式经常和Compsite模式结合起来表示一个层次式结构,这一层次式结构是一个共享叶节点的图。共享的结果是,Flyweight的叶节点不能存储指向父节点的指针。而父节点的指针将传给Flyweight作为它的外部状态的一部分。这对于该层次结构中对象之间的相互通讯的方式将产生很大的影响。


8、实现

        在实现Flyweight模式时,注意以下几点:

        1)删除外部状态    该模式的可用性在很大程度上取决于是否容易识别外部状态并将它从共享对象中删除。如果不同种类的外部状态和共享前对象的数目相同的话,删除外部状态不会降低存储消耗。理想的状况是,外部状态可以由一个单独的对象结构计算得到,且该结构的存储要求非常小。

        例如,在我们的文档编辑器中,我们可以用一个单独的结构存储排版布局信息,而不是存储每一个字符对象的字体和类型信息,布局图保持了带有相同拍板信息的字符的运行轨迹。当某字符绘制自己的时候,作为绘图遍历的副作用它接收排版信息。因为通常文档使用的字体和类型数量有限,将该信息作为外部信息来存储,要比内部存储高效得多。

        2)管理共享对象    因为对象是共享的,用户不能直接对它进行实例化,因此Flyweight-Factory可以帮助用户查找某个特定到底Flyweight对象。FlyweightFactory对象经常使用关联存储帮助用户查找感兴趣的Flyweight对象。例如,在这个文档编辑器一例中的Flyweight工厂就有一个以字符代码为索引的Flyweight表。管理程序根据所给的代码返回相应的Flyweight,若不存在,则创建一个Flyweight。

        共享还意味着某种形式的引用计数和垃圾回收,这样当一个Flyweight不再使用时,可以回收他的存储空间。然而,当Flyweight的数目固定而且很小的时候(例如,用户ACSII码的Flyweight),这两种操作都不必要。在这种情况下,Flyweight完全可以永久保存。


9、代码示例

        略。。。。。。



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值