为什么需要享元模式
在有些项目中,要重复用到多个对象,此时,为了节省资源,可以让重复的对象只生成一次。
例如,一个文档里面有30万个汉字,每个汉字都要显示出来,如果把每个汉字看成一个对象,要生成30万个对象,消耗内存。
但是已经发现,30万个汉字有很多重复的,最后要使用的汉字大概几千字,那么,只需要实例化几千个对象,让它们放在一个池中,要用到的时候,就取出来,节省内存。此时可以使用享元(Flyweight)模式。
注意
1.享元模式是一种常见的软件设计模式,对象称为享元。在该模式中,运用了共享技术有效地支持大量细粒度的对象。系统只使用少量的对象,状态变化很小,对象使用次数增多。
2.这里不详细介绍享元模式,只是介绍Java享元模式的一种实现,详细的定义等等可以自行搜索相关文档。
重要技术
1.享元池如何实现
享元池保存的是一个个享元,如果池中没有享元,则生成享元放到池中,如果有,则使用池中的享元。这里可以用使用Map,以享元关键字为key,享元对象为value,每个关键字和对象对应,保存在Map中。
但是,不能盲目向Map中添加数据,当一个享元第一次出现时,Map中并没有这个对象,此时,需要进行判断。如果不存在,则将享元放入Map,否则将从Map中取出该享元。
2.如何确定享元的关键字
特征完全相同的对象可以认为是同一个享元。“特征相同”这是一个比较复杂的问题:例如,在控制台上打印,两个汉字,内容相同,就可以用同一个对象表达;但是如果保存到word文件中,内容相同,还不能用同一个对象表达,必须颜色、大小等一系列的特征相同。
所以,享元的关键字必须能够表达一个享元的特征。
ps:下面代码为了简单起见将汉字的内容作为关键字,也就是说两个汉字内容相同,就用同一个对象表达。
代码编写
首先是享元类Word.java
package practice;
public class Word {
private String key;
public Word() {
System.out.println("实例化");
}
public void setKey(String key) {
this.key=key;
}
public void display() {
System.out.println(key+" 显示");
}
}
接下来是享元池类:
package practice;
import java.util.HashMap;
public class FlyweightPool {
private static HashMap<String,Word> pool=new HashMap<>();
public static Word getFlyweight(String key) {
Word word=(Word)pool.get(key);
if(word==null) {
word=new Word();
word.setKey(key);
pool.put(key,word);
}
return word;
}
}
最后是主函数所在模块,负责调用上面的两个模块:
package practice;
public class Main {
public static void main(String[] args) {
Word word1=FlyweightPool.getFlyweight("中");
Word word2=FlyweightPool.getFlyweight("华");
Word word3=FlyweightPool.getFlyweight("中");
Word word4=FlyweightPool.getFlyweight("国");
word1.display();
word2.display();
word3.display();
word4.display();
}
}
运行结果如下:
从中可以看出,实例化是三个对象而用了四次
注意
当以下情况成立时可以考虑使用享元模式:
1.一个应用程序使用了大量的对象。
2.由于使用大量的对象,造成很大的存储开销。
3.对象大多数状态都可变为外部状态(用关键字表达)。
享元模式可以大幅度地降低内存中对象的数量,但是,享元模式使得系统更加复杂。