定义
提供了减少对象数量从而改善应用所需的对象结构的方式,运用共享技术有效的支持大量细粒度的对象。简而言之,就是将类的通用属性抽出来,建立对象池,以达到限制对象数量的效果;一般用在需要多次创建对象的情况,如线程池、String常量池、数据库连接池、缓冲池等等。
适用场景
- 常常应用于系统底层的开发,以便解决系统新能问题
- 系统有大量相似对象,需要缓冲池的场景
例如如果一个系统存在大量细粒度对象,且这些对象的状态都可以外部化,就可以使用享元模式。下面是外部化和内部化的解释:
- 内部状态指对象共享出来的信息,存储在享元对象内部并且不会随环境的改变而改变;
- 外部状态指对象得以依赖的一个标记,是随环境改变而改变的、不可共享的状态。
优点
大大减少了对象的创建,降低了程序内存的占用,提高效率
缺点
提高了系统的复杂度。需要分离出内部状态和外部状态,而外部状态具有固化特性,不应该随着内部状态的改变而改变
UML类图
- Flyweight是抽象享元角色。它是产品的抽象类,同时定义出对象的外部状态和内部状态的接口或实现;
- ConcreteFlyweight是具体享元角色,是具体的产品类,实现抽象角色定义的业务;
- UnsharedConcreteFlyweight是不可共享的享元角色,一般不会出现在享元工厂中;
- FlyweightFactory是享元工厂,它用于构造一个池容器,同时提供从池中获得对象的方法。
代码演示
以职员做报告为例,每个部门都需要做报告,如果每次需要报告都要new一个报告,就会产生大大的减少性能。所以我们就可以用一个报告池专门用来让职员获取报告。
这里报告工厂就是享元工厂,报告就是抽象享元角色,不同的报告就是具体享元角色,在报告中固定不变的属性就是内部状态,需要改变的属性就是外部属性。
- 抽象Report类(Flyweight)
public abstract class Report {
//内部状态
private String content;
//外部状态
private final String department;
//要求享元角色必须接受外部状态
public Report(String department) {
this.department = department;
this.content = "部门报告";
}
//定义业务操作
public abstract void operate();
public String getContent() {
return content;
}
}
- 具体Report类(ConcreteFlyweight)
public class ConcreteReport extends Report {
public ConcreteReport(String department) {
super(department);
}
@Override
public void operate() {
System.out.println(super.getContent);
}
}
- ReportFactory(FlyweightFactory)
public class ReportFactory {
private final static Map<String,Report> map = new HashMap<>();
public Report getReport(String department){
Report report = map.get(department);
if (report == null){
report = new ConcreteReport(department);
map.put(department,report);
}
return report;
}
}
源码的应用
Integer类
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
...
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
可以看到如果i的值-128和127时,默认会去缓存池中获取,如果超过才会new一个返回,这里就用到了享元模式。