1.定义
使用共享对象可有效地支持大量的细粒度的对象。
是对象池技术的重要实现方式。
2.享元模式的使用场景
请看例子程序,学生和学校的关系:
package _22FlyweightPattern; /** * 班级类 */ public class School { // 学校id private int id; // 学校名称 private String schoolName; // 某学生对该学校的映像 private String desc; // 还有很多信息,比较占内存………… public int getId() { return id; } public void setId(int id) { this.id = id; } public String getSchoolName() { return schoolName; } public void setSchoolName(String schoolName) { this.schoolName = schoolName; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } }
package _22FlyweightPattern; /** * 学生类 */ public class Student { // 学生id private int id; // 学生姓名 private String name; // 学生家庭住址 private String homeAddress; // 所在学校 private School school; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getHomeAddress() { return homeAddress; } public void setHomeAddress(String homeAddress) { this.homeAddress = homeAddress; } public School getSchool() { return school; } public void setSchool(School school) { this.school = school; } }
由于多个学生可以共用一个School对象,因此我们使用享元模式来分析School类。
我们将一个可共用的细粒度对象(上例中的School对象)的信息分为两类
所谓的享元模式就是将一个类中可共享的部分抽取出来放进对象池,以避免重复创建相同(业务意义上的相同)的对象。
也就是下图所表示的,要将第一张图重构成第二张图:
3.享元模式的四个角色
享元角色就是可共享的角色。
下面是享元模式的类图:
4.享元模式的通用代码
package _22FlyweightPattern; /** * 抽象享元角色 */ public abstract class Flyweight { // 内部状态 private String intrinsic; // 外部状态 protected final String extrinsic; // 强制享元角色必须接受外部状态 public Flyweight(String extrinsic) { this.extrinsic = extrinsic; } public String getIntrinsic() { return intrinsic; } public void setIntrinsic(String intrinsic) { this.intrinsic = intrinsic; } // 定义业务逻辑 public abstract void operate(); }
package _22FlyweightPattern; /** * 具体享元角色 */ public class ConcreteFlyweight extends Flyweight { public ConcreteFlyweight(String extrinsic) { super(extrinsic); } @Override public void operate() { // 业务逻辑 } }
package _22FlyweightPattern; import java.util.HashMap; import java.util.Map; /** * 享元工厂 */ public class FlyweightFactory { // 定义一个池容器 private static Map<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 ConcreteFlyweight(extrinsic); pool.put(extrinsic, flyweight); } return flyweight; } }
5.享元模式的优缺点
享元模式是一个非常简单的模式,它可以大大减少应用程序创建的对象(比如你使用new School()的方式10000个学生对象就会产生10000个School对象,而通过缓存技术,那么只存在一个School对象),降低程序内存的占用,增强程序的性能,但它同时也提高了系统复杂性,需要分理处内部和外部状态,而且外部状态具有固化特效,不应该随内部状态改变而改变,否则导致系统的逻辑混乱。
6.最佳实践
外部状态最好使用Java的基本类型作为标志,如String、int等,可以大幅提升效率(Map的key值)。
享元模式在Java API中随处可见,比如String会对字符串进行缓存,Integer会对-128到127的数字进行缓存。String的intern方法:如果String对象池中有该类型的值,那么直接返回对象池中的对象。