享元模式(Flyweight Pattern)是一种结构型设计模式, 它摒弃了在每个对象中保存所有数据的方式, 通过共享多个对象所共有的相同状态, 让你能在有限的内存容量中载入更多对象。
ps:动态规划是嘛?
介绍
- 意图:运用共享技术有效地支持大量细粒度的对象。
- 主要解决:在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。
- 何时使用:
- 系统中有大量对象。
- 这些对象消耗大量内存。
- 这些对象的状态大部分可以外部化。
- 这些对象可以按照内蕴状态分为很多组,当把外蕴对象从对象中剔除出来时,每一组对象都可以用一个对象来代替。
- 系统不依赖于这些对象身份,这些对象是不可分辨的。
- 应用实例:
- 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
36package 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);
}
} - 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
29package 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);
}
} - 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
27package 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();
}
}
} - Main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22package 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可以被复用