定义:
- 使用共享对象可有效地支持大量的细粒度的对象。
将对象信息划分为: 内部状态(intrinsic)、外部状态(extrinsic)
内部状态:内部状态是对象可以共享出来的信息,存储在享元对象内部并且不会随环境改变而改变,它们可以作为一个对象的动态附加信息,不必直接存储在某个具体对象中,属于可以共享的部分。
享元模式的通用类图外部状态:是对象得以依赖的一个标记,是随环境改变而改变的、不可以共享的状态,他是一批对象的统一标识,是唯一的一个索引值。
public abstract class Flyweight {
private String intrinsic;
public String getIntrinsic() {
return intrinsic;
}
public void setIntrinsic(String intrinsic) {
this.intrinsic = intrinsic;
}
protected final String extrinsic;// final
// 享元角色接受外部的状态
public Flyweight(String _extrinsic) {
// TODO Auto-generated constructor stub
this.extrinsic = _extrinsic;
}
// 定义业务操作
public abstract void operate();
}
public class ConcreteFlyweightOne extends Flyweight {
public ConcreteFlyweightOne(String _extrinsic) {
super(_extrinsic);
// TODO Auto-generated constructor stub
}
@Override
public void operate() {
// TODO Auto-generated method stub
}
}
public class ConcreteFlyweightTwo extends Flyweight {
public ConcreteFlyweightTwo(String _extrinsic) {
super(_extrinsic);
// TODO Auto-generated constructor stub
}
@Override
public void operate() {
// TODO Auto-generated method stub
}
}
/*
* 创建享元对象工厂
* */
public class FlyweightFactory {
// 定义对象池
private static HashMap<String, Flyweight> pool = new HashMap<>();
// 享元工厂
public static Flyweight getFlyweight(String extrinsic) {
Flyweight flyweight = null;
if (pool.containsKey(extrinsic)) {
flyweight = pool.get(extrinsic);
} else {
// 创建享元对象,并放入pool中
flyweight = new ConcreteFlyweightOne(extrinsic);
pool.put(extrinsic, flyweight);
}
return flyweight;
}
}
优点和缺点:
- 大大减少应用程序创建的对象,降低程序内存的占用,增强程序的性能,但同时也提高了系统的复杂性,需要分离出外部状态和内部状态,而且外部状态具有固化特性,不应该随内部状态改变而改变,否则导致系统的逻辑混乱。
使用场景:
- 系统存在大量的相似对象
- 细粒度的对象都具备接近的外部状态,而且内部状态与环境无关,对象没有特定身份
- 需要缓冲池的场景
享元模式拓展
- 线程安全的问题:我们在使用享元模式的时候,对象池中的享元对象尽量多,多到足够满足业务为止
- 性能平衡:尽量使用java基本类型作为外部状态(如果把一个对象作为Map类的键值,一定要确保重写了equals和hashCode方法,否则会出现搜索失败的情况)
最佳实践:
public class Test {// api 中的享元模式
public static void main(String[] args) {
String str1 = "和谐";
String str2 = "社会";
String str3 = "和谐社会";
String str4;
str4 = str1 + str2;
System.out.println(str4 == str3);
str4 = (str1 + str2).intern();
System.out.println(str4 == str3);
}
}
>false
>true
虽然可以使用享元模式来实现对象池,但是二者还是有比较大的区别,对象池着重在对象的复用上,对象池中的每个对象都是可替换的,从对象池中取出的对象a、对象b对客户端来说是完全相同的,主要解决复用,而享元模式主要解决的是对象的共享问题,如何建立多个可共享的细粒度对象则是其关注的重点。
网友评论