定义:
采用一个共享来避免大量拥有相同内容对象的开销。这种开销中最常见、直观的就是内存的损耗。享元模式以共享的方式高效的支持大量的细粒度对象。
享元的英文是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对象而不是创建一个新对象,这就是享元模式的应用。
- java.lang.Integer#valueOf(int)
- java.lang.Boolean#valueOf(boolean)
- java.lang.Byte#valueOf(byte)
- java.lang.Character#valueOf(char)
举个栗子:
定义抽象享元角色
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(); }
输出