《图解设计模式》读书笔记9-1 Flyweight模式

模式简介

Flyweight是轻量的意思,此模式将要用的对象放到“对象池”中,随取随用,不重复创建对象,达到减少对象内存占用的目的,实现了对象的“轻量”。

在数据库连接池、字符串缓存池里面都有此模式的应用。

此模式的中文表述为:享元模式

示例代码

代码功能与实现思路

输入一个数字字符串,比如“121”,以"大型字符"形式输出出来。

输出的“大型字符”如下所示:

......##........
..######........
......##........
......##........
......##........
......##........
..##########....
................
....######......
..##......##....
..........##....
......####......
....##..........
..##............
..##########....
................
......##........
..######........
......##........
......##........
......##........
......##........
..##########....
................

一个“大型字符”就是一个很长的字符串,就是“重量级对象”,如果输入一串数字比如“1654843987461935”,把每一个数字对应的“大型字符”都创建成对象,内存占用会很可观。

观察可知,数字串里只包含0到9共十个数字,因此可以将数字对应的“大型字符”存到一个“对象池”里,每个对象只保存一份,用的时候取出来即可。这样可以大大减少内存占用。

类图

代码

BigChar:表示一个数字对应的“大型字符”,charname是数字字符,fontdata是存储“大型字符”的字符串。

构造函数从文件里面读取“大型字符”的字符串数据保存到fontdata里面。

public class BigChar {
    // 字符名字
    private char charname;
    // 大型字符对应的字符串(由'#' '.' '\n'组成)
    private String fontdata;
    // 构造函数
    public BigChar(char charname) {
        this.charname = charname;
        try {
            //从文件里面读取
            BufferedReader reader = new BufferedReader(
                new FileReader("D:\\" + 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) {
            this.fontdata = charname + "?";
        }
    }
    // 显示大型字符
    public void print() {
        System.out.print(fontdata);
    }
}

BigCharFactory:“大型字符”的工厂,pool就是对象池。使用了单例模式,整个程序里面只有一个工厂,工厂里面有一个对象池,需要“大型字符”就到对象池里面取,如果对象池里面有对应的“大型字符”则直接给予,没有就创建一个再给。

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;
    }
}

BigString:将数字字符串以“大型字符”形式输出。

public class BigString {
    // “大型字符”的数组
    private BigChar[] bigchars;
    // 构造函数
    public BigString(String string) {
        bigchars = new BigChar[string.length()];
        BigCharFactory factory = BigCharFactory.getInstance();
        for (int i = 0; i < bigchars.length; i++) {
            bigchars[i] = factory.getBigChar(string.charAt(i));
        }
    }
    // 显示
    public void print() {
        for (int i = 0; i < bigchars.length; i++) {
            bigchars[i].print();
        }
    }
}

Main

public class Main {
    public static void main(String[] args) {
        BigString bs = new BigString("1212123");
        bs.print();
    }
}

结果图示分析

模式角色和类图

角色

  • Flyweight

Flyweight表示那些实例会被共享的类。在本例中,由BigChar扮演此角色。

  • FlyweightFactory

FlyweightFactory角色是生成Flyweight角色的工厂。在工厂中生成Flyweight角色可以实现实例共享。在本例中,由BigCharFactory类扮演此角色。

  • Client

Client角色使用FlyweightFactory类的单例实例生成Flyweight角色并使用之。在本例中,由BigString类扮演此角色。

类图

拓展思路

对多个地方产生影响

共享对象,意味着一旦对象发生改变,所有引用它的地方都要变,注意这个特点,修改之前要考虑好造成的影响。

什么要共享,什么不要共享

应当共享的信息被称为Intrinsic,表示本质的,固有的。即不依赖于实例状态,无论在什么情况下都不会改变的信息,可以被共享。

不应当共享的信息被称为Extrinsic,表示外在的,非本质的。即随时可能变化的信息,不应该被共享。

垃圾回收

如果对象占用了过多内存,JVM就要开始回收垃圾了。如果一个对象没有被任何实例引用,垃圾回收器就会认为这是个垃圾,会回收掉。在上面例子中,由于pool是HashMap,里面的共享对象始终会被pool引用,所以不会被回收。

posted on 2019-07-19 20:26  vplus  阅读(153)  评论(0编辑  收藏  举报