`
Dapple
  • 浏览: 101682 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

亨元模式(Flyweight pattern)

阅读更多
翻译为亨元模式,或直译为轻量级模式。所谓亨元,就是被其它对象共享使用的
对象。通过这种方式,系统减少了内存的占用。比如一个系统有一个成百上千、
成千上万的小对象,这些小对象可能拥有着共同的信息,那么这些共同的信息就
没有必要也重复成千上万次。把这些共同信息抽取出来,形成一个对象,即是亨
元。这些成千上万的其它对象只需要引用这个亨元即可。

举个例子,在棋类程序中,有时候我们会把一个棋子当成为一个对象。这个对象
包含着位置信息、字体信息、颜色信息、样式信息等。如下所示,

class 棋子{
  public 名字(车、马、炮等)
  public 位置信息
  public 字体信息
  public 颜色信息
  public 样式信息
}


如果我们每次new一个这样的对象,它所占用的内存是这些信息所占内存的和。
new 10个这样的对象就需要10倍的内存占用。实际上,在象棋中,除了象棋棋子
的名字不同、位置不同外,其它的字体信息、样式信息一般都是一样的。比如红
方的棋子是红色、隶书、字体有阴影效果,黑方的棋子是黑色、隶书、有阴影效
果。

这些字体信息、颜色信息、样式信息都可以做成亨元。拿颜色信息来说,如果不
使用亨元,则每一个棋子都要分别创建一个颜色对象,10个棋子就创建10个颜色
对象,而这10个颜色对象很可能是一样的,比如都是红色。这样就太浪费内存了。
使用亨元,则可以避免这种重复创建相同对象的问题。

我们只需要创建一个红色信息的对象,而让所有红色棋子都引用这个红色对象。
同理,让所有黑色棋子都引用黑色对象。这样就减少了内存使用。

比如,我们在new 一个红色棋子对象时,要先去一个颜色信息池中区查找是否已
经有这么一个红色对象,有则,直接引用它,没有则先创建它再引用它。所谓颜
色信息池,一般都用Map来实现。比如,

Map h = new HashMap();
if(h.get("red")!=null){
    h.pub("red",new Color对象);
}else{
    取出这个Color对象(我们叫它objectC)。
}


然后你在另一个地方把这个objectC赋给棋子对象,比如用这种方式:
棋子对象.颜色信息=objectC;

实际上,针对象棋这个例子也没有必要用一个pool来保存Color对象,也没有必要
判断这个对象是不是在pool中存在,也没必要在判断完是否存在后再进行是否生
成一个新对象的工作。因为象棋中无非两种对象,红和黑,这是确定的。所以我
们完全可以把这两个对象提前生成出来,放到一个地方保存着,别人都对它们进
行引用即可,可以这样做,

class 棋子{
    private static Color redColor = new Color("red");
    private static Color blackColor = new Color("black");
...
...      
}


这好像有点单态模式的感觉。实际上,一个class会共享很多实例而不像单态那样
只允许共享一个实例,也不像象棋这样只有红、黑两种颜色对象。假如一个应用
中,class共享的颜色对象可能除了红、黑,还有其它上百种颜色,而且颜色种类
的增减,依赖于用户的操作,那么用一个pool把它们存放起来就比较好。

一般亨元的获取都是通过一个工厂类来实现,例子如下:

class A{
    private B b;
    private C c;
    private Flyweight f;
    public A(B b,C c,Flyweight f){
        this.b = b;
        this.c = c;
        this.f = f;
    }
}

class Factory{
    private static Factory f = new Factory();
    private static Map m = new HashMap();
    private Factory(){};
    public Factory getInstance(){return f;}
    public void getFlyweight(Object key){
        Flyweight fly;
        if(m.get(key)==null){
             fly = new Flyweight(key);
             f.put(key,fly);
        }else{
             fly = (Flyweight)f.get(key);
        }
        return fly;
     }
}

class Client{
    public static void main(String args[]){
        B b = new B();
        C c = new C();
        Flyweight f = Factory.getInstance().getFlyweight(key);
        A a = new A(b,c,f);
    }
}


下面3个类的具体内容不重要,略
class B
class C
class Flyweight

当用户new一个A的实例,A中的Flyweight对象只是指向共享对象的一个引用。在
这个例子中,使用HashMap来保存共享对象,随着程序的运行,HashMap存的对象
会越来越多,HashMap自己会变的越来越大。有的时候,因为用户的操作也可能会
使对象被杀掉(比如棋子被吃掉),以至于有的亨元没有被任何对象引用,这个
时候,如果本身已经有点臃肿的HashMap能瘦身,自动释放亨元,对系统性能的提
升也非常有好处。比较简单的一个办法就是用WeakHashMap代替HashMap.

Flyweight模式是一种Cache技术,站在更高的层面看,它是一种资源使用技术。
这种技术更多的见于对性能要求比较高的底层开发领域。Java语言中的某县内置
对象可能就是用Flyweight模式实现的。比如最常用的String对象就可能是如此,
可以做一个简单测试,如下,
public class Test {
public static void main(String[] args) {
    String a1 = "aaa"
    String b1 = "bbb";
    String a2 = "aaa";
    String b2 = "bbb"; 
    System.out.println(a1 == a1);
    System.out.println(b1 == b2);                                     
    String c = a + b;
    System.out.println(c == "aaabbb");
    String flyweight = (a + b).intern();//这里用到了亨元模式. 返回"aaabbb"的一个引用。
    System.out.println(flyweight == "aaabbb"); 
  }
}
程序分别返回 true,true,false,true.  (==用来测试两者是不是引用同一个实例).

String在java中的应用甚为频繁,所以使用Flyweight模式以提高性能理所当然,
但事实上String很多地方并未使用该模式,比如

String a =new String("aaa");
String b = new String("bbb");
System.out.println(a == b);


程序打印出false.

或许这是Java为开发者故意提供的一个让同一个字符串有多个实例的选择。

最后要注意的一点是,这些共享的对象有可能在途中被改变。你要根据不同的应
用来决定是不是允许共享对象改变,如果不允许就把它们设计成不可变对象
(immutable)。如果允许变,则要考虑是不是创建个新的亨元同时保留老亨元,或
者通知那些使用亨元的对象亨元已经改变了。根据你的具体情况具体分析具体设
计。
1
2
分享到:
评论
1 楼 lixia0417 2010-08-07  
不错,楼主解释得挺详细的,我认为可能数据库连接池就是用的类似方法实现的;

相关推荐

    您的设计模式

    23. 亨元模式(Flyweight Pattern) 亨元模式使用共享对象可有效地支持大量的细粒度的对象。 24. 备忘录模式(Memento Pattern) 备忘录模式在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这...

    24种设计模式介绍与6大设计原则

    16. 亨元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度的对象。 17. 享元模式(享元模式在文档中出现了两次,这里默认为漏识别,实际上享元模式是设计模式中的一种)。 18. 备忘录模式(Memento ...

    23中设计模式

    23. 亨元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度的对象。 24. 备忘录模式(Memento Pattern):在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将...

    Java设计模式经典搞笑珍藏版

    #### 二十四、亨元模式(Flyweight Pattern) 亨元模式运用共享技术有效地支持大量细粒度的对象。 #### 二十五、备忘录模式(Memento Pattern) 备忘录模式在不破坏封装性的前提下,捕获一个对象的内部状态,并在该...

    Java23种设计模式讲解

    23. 亨元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度的对象。 除了上述23种设计模式外,书中还讲解了六大设计原则,它们是面向对象设计的基本原则,可用来提高代码的可复用性、灵活性和可维护性...

    外文翻译:学用JavaScript设计模式

    - **亨元(Flyweight)模式**:用于减少创建大量相似对象所需的内存消耗。 #### JavaScript MV* 模式 随着前端框架的发展,MV*(Model-View-*)模式成为了现代Web开发中的重要组成部分。本书中介绍的几种MV*模式...

Global site tag (gtag.js) - Google Analytics