#1.定义
使用共享对象可有效的支持大量的细粒度的对象。
享元模式是池技术的重要实现方式,享元模式的定义为我们提出了两个要求,细粒度对象和共享对象。我们知道分配太多的对象到以程序中将有损程序的性能,还会造成内存溢出,享元模式正是为此而生的。
说到细粒度对象,不可避免地使得对象数量多且性质相近,可以将对象信息分为两个部分:内部状态(intrinic)与外部状态(extrinisic)
- 内部状态
对象可以共享出来的信息,存储在对象内部并不会随着环境的改变而改变,它们可以作为一个对象的动态附加信息,不必直接存储在具体某个对象中,属于可以共享的部分。 - 外部状态
是对象得以依赖的一个标记,是随环境改变而改变的、不可以共享的状态。
#2.类图
##角色介绍 - Flyweight 抽象享元角色:一个产品的抽象类,同时定义出对象的内部状态和外部状态的接口或实现。
- ConcreteFlyweight 具体享元角色:具体的一个产品类,实现抽象角色定义的业务。该角色中需要注意的是内部状态处理应该与环境无关,不应该出现一个操作改变了内部状态,同时改变了外部状态,绝对不允许。
- unsharedConcreteFlyweight 不可共享的享元角色:不存在外部状态或者安全要求(如线程安全)不能够使用共享技术的对象,该对象一般不会出现在享元工厂中。
- FlyweightFactory 享元工厂:构造一个池容器,同时提供从池中获得对象的方法
#3.通用源码
抽象享元角色
public abstract class Flyweight {
//内部状态
private String intrinsic;
//外部状态
protected final String Extrinsic;
//要求享元角色必须接受外部状态
public Flyweight (String _Extrinsic) {
this.Extrinsic = _Extrinsic;
}
//定义业务操作
public abstract void operate();
//内部状态的getter/setter
public String getIntrinsic() {
return intrinsic;
}
public void setIntrinsic(String intrinsic) {
this.intrinsic = intrinsic;
}
}
具体享元模式
public class ConcreteFlyweight1 extends Flyweight {
//接受外部状态
public ConcreteFlyweight1(String _Extrinsic) {
super(_Extrinsic);
}
//根据外部状态进行逻辑处理
public void operate() {
//业务逻辑
}
}
public class ConcreteFlyweight2 extends Flyweight {
//接受外部状态
public ConcreteFlyweight2(String _Extrinsic) {
super(_Extrinsic);
}
//根据外部状态进行逻辑处理
public void operate() {
//业务逻辑
}
}
享元工厂
public class FlyweightFactory {
//定义一个池容器
private static HashMap<String,Flyweight> pool = new HashMap<String,Flyweight>();
//享元工厂
public static Flyweight getFlyweight(String Extrinsic) {
//要返回的对象
Flyweight flyweight = null;
//在池中没有该对象
if(pool.containsKey(Extrinsic)) {
flyweight = pool.get(Extrinsic);
}else {
//根据外部状态创建享元对象
flyweight = new ConcreteFlyweight1(Extrinsic);
}
return flyweight;
}
}
#4.Demo
假设现在有一个报考系统,有一个模块负责社会人员报名,该模块只开放三天,并限制报考人员的数量,两天系统宕了4次。原因是每个人报名都会建立一个对象,对象太多,导致内存耗尽,现在用享元模式,优化。
报考信息类
public class SignInfo {
private String id; // 报名人员的id
private String location; // 考试地点
private String subject; // 考试科目
private String postAddress; // 邮寄地址
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getPostAddress() {
return postAddress;
}
public void setPostAddress(String postAddress) {
this.postAddress = postAddress;
}
}
带对象池的报考信息
public class SignInfo4Pool extends SignInfo {
//定义一个对象池提取的key值
private String key;
//构造函数获得相同标志
public SignInfo4Pool(String _key) {
this.key = _key;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
}
带对象池的工厂类
public class SignInfoFactory {
//池容器
private static HashMap<String,SignInfo> pool = new HashMap<String,SignInfo>();
//报名信息工厂
@Deprecated
public static SignInfo getSignInfo() {
return new SignInfo();
}
//从池中获取对象
public static SignInfo getSignInfo(String key) {
SignInfo result = null; //设置返回对象
//池中没有该对象,则建立,放入池中
if(!pool.containsKey(key)) {
System.out.println(key + "---建立对象,并放入到池中");
result = new SignInfo4Pool(key);
pool.put(key, result);
}else {
result = pool.get(key);
System.out.println(key + "---直接从池中取得");
}
return result;
}
}
场景类
public class Client {
public static void main(String[] args) {
//初始化对象池
for (int i = 0; i < 4; i++) {
String subject = "科目"+i;
//初始化地址
for (int j = 0; j <30; j++) {
String key = subject + "考试地点" + j;
SignInfoFactory.getSignInfo(key);
}
}
SignInfo signInfo = SignInfoFactory.getSignInfo("科目1考试地点1");
}
}
运行结果:
最后一个对象,直接从容器里取的,这个对象是带有科目和考试地点,之后每建一个对象都会享有科目和考试信息。
#5.应用场景
- 系统中存在大量相似的对象
- 细粒度的对象都具备较接近的外部状态,而且内部状态与环境无关,也就是说对象没有特定身份
- 需要缓冲池
#6.优缺点 - 优点
- 减少应用程序创建对象,降低程序内存的占用
- 增强程序的性能
- 缺点
- 提高了系统复杂性
- 需要分离出外部状态和内部状态,而且外部状态具有固定化性,不应随内部状态的改变而改变,否则会导致系统的逻辑混乱。
参考资料 01. 秦晓波.《设计模式之禅》. 机械工业出版社 :2010年