0%

设计模式之Flyweight模式

享元模式(Flyweight Pattern)是一种结构型设计模式, 它摒弃了在每个对象中保存所有数据的方式, 通过共享多个对象所共有的相同状态, 让你能在有限的内存容量中载入更多对象。

ps:动态规划是嘛?

介绍

  • 意图:运用共享技术有效地支持大量细粒度的对象。
  • 主要解决:在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。
  • 何时使用:
    • 系统中有大量对象。
    • 这些对象消耗大量内存。
    • 这些对象的状态大部分可以外部化。
    • 这些对象可以按照内蕴状态分为很多组,当把外蕴对象从对象中剔除出来时,每一组对象都可以用一个对象来代替。
    • 系统不依赖于这些对象身份,这些对象是不可分辨的。
  • 应用实例:
    • JAVA 中的 String,如果有则返回,如果没有则创建一个字符串保存在字符串缓存池里面。
    • 数据库的数据池。

      示例

  1. Bigchar(单个字符所表达的类)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    package com.edu.tju.GOF.Flyweight;

    import java.io.BufferedReader;
    import java.io.FileReader;
    import java.io.IOException;

    public class BigChar {
    // 字符名字
    private char charname;
    // 大型字符对应的字符串(由'#' '.' '\n'组成)
    private String fontdata;
    // 构造函数
    public BigChar(char charname) {
    this.charname = charname;
    try {
    BufferedReader reader = new BufferedReader(
    new FileReader("big" + charname + ".txt")
    );
    String line;
    StringBuffer buf = new StringBuffer();
    while ((line = reader.readLine()) != null) {
    buf.append(line);
    buf.append("\n");
    }
    reader.close();
    this.fontdata = buf.toString();
    } catch (IOException e) {
    e.printStackTrace();
    this.fontdata = charname + "?";
    }
    }
    // 显示大型字符
    public void print() {
    System.out.print(fontdata);
    }
    }
  2. BigCharFactory类
    我们用到了单例模式来实现BigCharFactory类,因为我们只需要一个BigCharFactory类的实例就可以了.同时我们使用synchroized关键字修饰getBigChar方法,防止多线程对 生成BigChar实例的影响.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    package com.edu.tju.GOF.Flyweight;

    import java.util.HashMap;

    public class BigCharFactory {
    // 管理已经生成的BigChar的实例
    private HashMap pool = new HashMap();
    // Singleton模式
    private static BigCharFactory singleton = new BigCharFactory();
    // 构造函数
    private BigCharFactory() {
    }
    // 获取唯一的实例
    public static BigCharFactory getInstance() {
    return singleton;
    }
    // 生成(共享)BigChar类的实例
    public synchronized BigChar getBigChar(char charname) {
    BigChar bc = (BigChar)pool.get("" + charname);
    if (bc == null) {
    bc = new BigChar(charname); // 生成BigChar的实例
    pool.put("" + charname, bc);
    }
    return bc;
    }
    public BigChar getBigCharNotUsed(char name){
    return new BigChar(name);
    }
    }
  3. BigString类
    我们将构造函数中 设置isUsed来观察使用与不使用Flyweight的区别
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    package com.edu.tju.GOF.Flyweight;

    public class BigString {
    // “大型字符”的数组
    private BigChar [] bigchars;
    public BigString(String word,boolean isUsed){
    if(isUsed == true){
    bigchars=new BigChar[word.length()];
    BigCharFactory bf=BigCharFactory.getInstance();
    for(int i=0;i<word.length();i++){
    bigchars[i]=bf.getBigChar(word.charAt(i));
    }
    }else{
    bigchars=new BigChar[word.length()];
    BigCharFactory bf=BigCharFactory.getInstance();
    for(int i=0;i<word.length();i++){
    bigchars[i]=bf.getBigCharNotUsed(word.charAt(i));
    }
    }
    }

    public void print(){
    for(int i=0;i<bigchars.length;i++){
    bigchars[i].print();
    }
    }
    }
  4. Main
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    package com.edu.tju.GOF.Flyweight;


    public class Main {

    public static void main(String[] args) {
    String name="2222222222222222222222222222222222222222222222";
    testMemory( name, false);
    testMemory( name, true);
    }
    public static void testMemory(String name,boolean isUsed){
    System.out.println("是否使用轻量级:"+isUsed);
    BigString bs=new BigString(name,isUsed);
    bs.print();
    countMemory();
    System.out.println("=================");
    }
    public static void countMemory(){
    Runtime.getRuntime().gc();
    System.out.println("已使用内存:"+(Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory()));
    }
    }


    我们对比了没有使用享元的例子,可以发现所占用的内存空间,明显使用了享元的占用的内存小,而没有使用享元的占用的内存多.并且这里我们要注意垃圾回收机制,在工厂类中,使用了HashMap来将BigChar对象保存起来,这样就形成了一个DAC(有向无环图),只要pool变量不被释放,我们使用的共享单元是不会被释放的。这样就保证了BigChar对象数组不被释放,在使用享元模式的时候一定要特别注意这种情况,因为垃圾回收器(GC)在内存占用过多的时候被唤醒,然后清理那些被再被使用的内存,采用的方式就是DAC。

    角色

  • Flyweight(共享类,轻量级)
  • FlyweightFactory(共享类工程)
  • Client(调用者)

中心思想:FlywightFactory通过pool(共享池)来保存共享Flyweight(单例模式),使得Flyweight可以被复用

Thank you for your support