运用共享技术支持大量细粒度的对象,减少对象创建时间,同时节省内存的消耗,从而提高系统性能
介绍
享元模式的关键就是共享对象,要理解共享对象,我们先来理解下面2个词语:
- 内部状态:不会随环境改变而改变的共享部分
- 外部状态:随环境改变而改变部分、以及不可以共享的部分
那么享元模式的共享的元素就是内部状态,但是系统中,我们很难找到完全一样的对象啊,那我们该怎么共享?
这就是理解享元的关键了,我们需要将这些对象创建的更细粒,也就是将它分为“内部状态”和“外部状态”。
这样我们就可以将内部状态共享了,下面看个例子。(没有UML图哦,之后补上)
场景模拟
学校5年级有2个班A、B班,A班和B班分布有100个人,这个时候A、B班要分别发布春游计划给本班同学看,A、B班级的春游计划是不一样的,所以A班同学只看A班的春游计划,而春游计划放在数据库中,我们防止A班同学每次看春游计划的时候都查询并生成一个新的春游计划的对象,所以我们在A班第一个同学查看春游计划后,就将计划存入内存中,方便之后的同学查看,但是每个春游计划中的同学名称是不一样的,此时,我们就可以将春游计划设定为内部状态,而对于的同学信息就是外部状态
主要角色
- 享元工厂 : 为A、B班同学管理春游计划
- 抽象享元 : 为工厂提供统一的接口
- 具体的享元类 : 从数据库中读取的“春游计划类”
- 外部状态:学生类
- 主方法 : 模拟张三同学读取春游计划
代码实现
- 抽象享元, 为工厂提供统一的接口
public interface IPlanManager {
public String readPlan(Student str);
}
- 具体的享元类 ,从数据库中读取“春游计划”
public class PlanForAManager implements IPlanManager {
protected String planId=null;
public PlanForAManager(String planId){
this.planId = planId;
}
@Override
public String readPlan(Student str) {
return str.getName() + "同学,早上7点半到大门集合 ………………";
}
}
public class PlanForBManager implements IPlanManager {
protected String planId=null;
public PlanForBManager(String planId){
this.planId = planId;
}
@Override
public String readPlan(Student str) {
return str.getName() + "同学,中午11点半到学校后门先吃饭 ………………";
}
}
- 非共享元,也就是外部状态
public class Student{
private String name;
public Student(String name){
this.name = name;
}
public String getName(){
return this.name;
}
}
- 享元工厂 ,为A、B班同学管理春游计划
import java.util.HashMap;
import java.util.Map;
public class PlanManagerFactory {
Map<String ,IPlanManager > planForManager = new HashMap<String ,IPlanManager >();
IPlanManager getPlanForAManager(String planId){
IPlanManager r = planForManager.get(planId);
if(r == null){
r = new PlanForAManager(planId);
planForManager.put(planId, r);
}
return r;
}
IPlanManager getPlanForBManager(String planId){
IPlanManager r = planForManager.get(planId);
if(r == null){
r = new PlanForBManager(planId);
planForManager.put(planId, r);
}
return r;
}
}
- 主方法, 模拟同学读取春游计划
public class Demo{
public static void main(String[] args) {
PlanManagerFactory pmf = new PlanManagerFactory ();
Student student = new Student("张三");
IPlanManager rm = pmf.getPlanForAManager("spring_plat");
System.out.println(rm.readPlan(student));
}
}
和对象池的区别
- 对象池:所有对象都是等价的,任意两个对象的使用场景都可以被对方的其他对象替代
- 享元工厂:相同的对象只保存一个,所有对象都是不同的,任意两个对象间不能相互替代
总结
我们平时使用的String对象,就是享元模式,因为当创建一个和之前String内容一样的新的String对象时,系统是不会创建新的String对象的,而是用新的指向旧的String。
使用条件
- 如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大的存储开销时就应该考虑使用享元模式
- 对象的大多数状态可以外部状态,如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象,此时可以考虑使用享元模式
缺点
- 享元模式需要一个维护共享对象的工厂,而这个工厂也将消耗系统资源
- 将一个类分为内部对象和外部对象时,也是的系统逻辑更加复杂,所以没有大量共享元素时,需要谨慎使用享元模式。