定义:
采用一个共享来避免大量拥有相同内容对象的开销。这种开销中最常见、直观的就是内存的损耗。享元模式以共享的方式高效的支持大量的细粒度对象。
享元的英文是flyweight,是一个来自体育方面的专业用语,在拳击、摔跤和举重比赛中特指最轻量的级别。把这个单词移植到软件工程中,也是用来表示特别小的对象,即细粒度的对象。至于为什么把flyweight翻译为享元,可以理解为共享元对象,也就是共享细粒度对象。
享元模式中区分了内蕴状态和外蕴状态。内蕴状态不能改变,是可以共享的。外蕴状态是可以的改变的,不能共享,由客户端保持。
模式组成:
- Flyweight: 抽象享元类。所有具体享元类的超类或者接口,通过这个接口,Flyweight可以接受并作用于外部专题
- ConcreteFlyweight: 具体享元类。指定内部状态,为内部状态增加存储空间
- FlyweightFactory: 享元工厂类。用来创建并管理Flyweight对象,它主要用来确保合理地共享Flyweight,当用户请求一个Flyweight时,FlyweightFactory就会提供一个已经创建的Flyweight对象或者新建一个(如果不存在)。
uml类图:
模式分类:
- 单纯享元模式(即该对象没有外蕴状态,整个对象可以共享,类似于单例模式)
- 复合享元模式(该对象有内蕴状态,也有外蕴状态,内蕴状态共享)
优点:
- 极大的减少系统中对象的个数,节省内存的开销
- 避免创建过多的对象,提升性能
缺点:(这也算缺点?有待考究)
- 由于享元模式需要区分外部状态和内部状态,使得应用程序在某种程度上来说更加复杂化了
-
为了使对象可以共享,享元模式需要将享元对象的状态外部化,而读取外部状态使得运行时间变长
应用场景:
- 存在大量重复对象的场景
在Java中,lang包下的Integer类,对于经常使用的-128 到 127 范围内的Integer对象当类一被加载时就被创建了,并保存在cache数组中,一旦程序调用valueOf 方法,如果i的值是在-128 到 127 之间就直接在cache缓存数组中去取Integer对象而不是创建一个新对象,这就是享元模式的应用。
举个栗子:
定义抽象享元角色
abstract class Dish {
public abstract void desc();
}
定义具体享元角色
class ButterMoshroom extends Dish {
public String taste;
public ButterMoshroom(String taste) {
this.taste = taste;
}
@Override
public void desc() {
System.out.println(taste + "酱野山菌");
}
}
定义工厂
class ButterMoshroomFactory {
static Map<String, Dish> dishes = new HashMap<String, Dish>();
public synchronized static Dish getDish(String taste) {
Dish dish = dishes.get(taste);
if (dish == null) {
dish = new ButterMoshroom(taste);
}
dishes.put(taste, dish);
return dish;
}
}
客户端调用
public static void main(String[] args) {
ButterMoshroomFactory.getDish("xo").desc();
ButterMoshroomFactory.getDish("番茄").desc();
}
输出