—摘要—

面向对象技术可以很好地解决一些灵活性或可扩展性问题。享元模式主要应用于创建过多的对象或者类似的情况,但在很多情况下需要在系统中增加类和对象的个数,当对象数量太多时,将导致运行代价过高,对服务器带来了相当大的压力,性能出现明显的下降。享元模式通过共享技术实现相同或相似对象的重用提高系统资源的利用率。本文首先阐述了享元模式要解决的问题和解决问题的理念,然后从实现角度重点说明了该模式的本质,并进一步给出了其所包含的角色和组织结构。最后,给出了共享模式的应用实例和使用场景以及相应的java实现。

Intent as stated by GoF is, “Use sharing to support large numbers of fine-grained objects efficiently”. Sharing

is key in flyweight pattern and we need to judiciously decide if this pattern can be applied.


—When to Use Flyweight Design Pattern—

我们会在下面的情况下选择享元模式:

  • 需要创建大量的对象。
  • Because of the large number when memory cost is a constraint.
  • 对象的大部分属性能够被扩展和
  • The application must not mandate unique objects, as after implementation same object will be used repeatedly.
  • Its better when extrinsic state can be computed rather than stored. (explained below)
享元模式因存储和共享而存在。虽然现在的应用计算机的硬件性能越来越强大,存储越来越大,效率越来越高,但对于性能的话题是永无止境的。


How to Apply Flyweight—


在我们要使用享元模式之前,需要对将要创建大量对象的Objective进行有效的分析。面向对象,利用对象的复用来减少对象的大量创建。

享元模式通过共享技术实现相同或相似对象的重用,示意图如下(我们可以共用一个 Hello world 对象,其中字符串 “Hello world” 为内部状态,可共享;字体颜色为外部状态,不可共享,由客户端设定):

             引子.png-29.6kB

  在享元模式中可以共享的相同内容称为 内部状态(Intrinsic State),而那些需要外部环境来设置的不能共享的内容称为 外部状态(Extrinsic State),其中外部状态和内部状态是相互独立的,外部状态的变化不会引起内部状态的变化。由于区分了内部状态和外部状态,因此可以通过设置不同的外部状态使得相同的对象可以具有一些不同的特征,而相同的内部状态是可以共享的。也就是说,享元模式的本质是分离与共享 : 分离变与不变,并且共享不变。把一个对象的状态分成内部状态和外部状态,内部状态即是不变的,外部状态是变化的;然后通过共享不变的部分,达到减少对象数量并节约内存的目的。

  在享元模式中通常会出现工厂模式,需要创建一个享元工厂来负责维护一个享元池(Flyweight Pool)(用于存储具有相同内部状态的享元对象)。在享元模式中,共享的是享元对象的内部状态,外部状态需要通过环境来设置。在实际使用中,能够共享的内部状态是有限的,因此享元对象一般都设计为较小的对象,它所包含的内部状态较少,这种对象也称为 细粒度对象。 
享元模式的目的就是使用共享技术来实现大量细粒度对象的复用。


—定义与结构—

 享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于 享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种 对象结构型模式

  Flyweight Pattern: Use sharing to support large numbers of fine-grained objects efficiently.


1、模式所涉及的角色

 Flyweight: 享元接口,通过这个接口传入外部状态并作用于外部状态; 
 ConcreteFlyweight: 具体的享元实现对象,必须是可共享的,需要封装享元对象的内部状态; 
 UnsharedConcreteFlyweight: 非共享的享元实现对象,并不是所有的享元对象都可以共享,非共享的享元对象通常是享元对象的组合对象; 
 FlyweightFactory: 享元工厂,主要用来创建并管理共享的享元对象,并对外提供访问共享享元的接口;


2、模式结构






Use of Flyweight in JDK—


java.lang.Integer#valueOf(int) (also on Boolean, Byte, Character, Short, Long, Float and Double). API doc says, "...this method is likely to yield significantly better space and time performance by caching frequently requested values....".
public static Integer valueOf(int i) {
        assert IntegerCache.high >= 127;
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
        }

        private IntegerCache() {}
    }

这儿的IntegerCache有一个静态的Integer数组,在类加载时就将-128 到 127 的Integer对象创建了,并保存在cache数组中,一旦程序调用valueOf 方法,如果i的值是在-128 到 127 之间就直接在cache缓存数组中去取Integer对象。

再看其它的包装器:

  • Boolean:(全部缓存)
  • Byte:(全部缓存)

  • Character(<= 127缓存)
  • Short(-128 — 127缓存)
  • Long(-128 — 127缓存)

  • Float(没有缓存)
  • Doulbe(没有缓存)

同样对于垃圾回收器来说:



—应用—
享元模式在编辑器软件中大量使用,如在一个文档中多次出现相同的图片,则只需要创建一个图片对象,通过在应用程序中设置该图片出现的位置,可以实现该图片在不同地方多次重复显示。

Design Pattern Related to Flyweight—

工厂模式、单例模式。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐