什么是享元模式
享元模式是指运用共享技术有效的支持大量细粒度对象的复用。系统只使用少量的对象,而这些都很相似,状态变化小,可以实现对象的多次复用。
享元模式中对象实现共享的关键是区分了内部状态和外部状态:
内部状态:存储在对象内部并且不会随环境改变而改变的状态,内部状态可以共享。
外部状态:随环境改变而改变,不可以共享的状态,是由客户端传入对象内部。多个外部状态之间是相互独立的。
正因为对象区分了内部状态和外部状态,所以可以将相同内部状态的对象存储在享元池中,享元池中的对象是可以共享的,需要的时候直接从享元池中获取,实现对象的复用。
享元模式包含以下几个角色:
Flyweight(抽象享元类):通常是一个接口或抽象类,声明一些可以向外界提供享元对象内部状态的方法,同时也可以通过这些方法设置内部状态。
ConcreteFlyweight(具体享元类):实现了抽象享元类,为内部状态提供了存储空间。
UnSharedConcreteFlyweight(非共享具体享元类):并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可以设计为非共享具体享元类,当需要使用非共享具体享元类时可以直接实例化创建。
FlyweightFactory(享元工厂类):享元工厂类用于创建并管理享元对象,将各个类型的的具体享元对象存储在一个享元池中,享元池一般设计为键值对集合结构。
标准的享元模式结构即包含可以共享的具体享元类,也包含不可以共享的非共享具体享元类。即分为两种特殊的享元模式,单纯享元模式和复合享元模式:
单纯享元模式:所有的具体享元类都是可以共享的,不存在非共享具体享元类。
复合享元模式:将一些单纯享元对象使用组合模式加以组合,还可以形成复合享元对象,这样的复合享元对象本身不能共享,但是可以分解成单纯享元对象,后者可以共享。
享元模式的优缺点
优点
- 可以极大的减少内部中对象的数量,使得相同或相似的对象在内存中只保存一份。节约系统资源。
- 享元模式的外部状态相对独立,而且不会影响其内部状态,使得享元对象可以在不同的环境中被共享。
缺点
- 享元模式使得系统变复杂,需要分离出内部状态和外部状态,使得程序逻辑复杂化。
- 为了对象可以共享,享元模式需要将享元对象的部分状态外部化,而读取外部状态会使运行时间变长。
享元模式的应用场景
- 一个系统有大量相同或相似的对象。
- 对象的大部分状态都可以外部化,可以将这些外部状态传入对象中。
享元模式的案例
public class User {
private String name;
public User(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
// 抽象享元类
public abstract class WebSite {
public abstract void use(User user);
}
// 具体享元类
public class ConcreteWebSite extends WebSite {
private final String type;
public ConcreteWebSite(String type) {
this.type = type;
}
@Override
public void use(User user) {
System.out.println("网站的发布形式为:" + type + " 在使用中 .. 使用者是" + user.getName());
}
}
// 享元工厂类
public class WebSiteFactory {
private final ConcurrentHashMap<String, ConcreteWebSite> pool = new ConcurrentHashMap<>();
/**
* 根据网站的类型,返回一个网站, 如果没有就创建一个网站,并放入到池中,并返回
*
* @param type type
* @return WebSite
*/
public WebSite getWebSiteCategory(String type) {
if (!pool.containsKey(type)) {
//就创建一个网站,并放入到池中
pool.put(type, new ConcreteWebSite(type));
}
return pool.get(type);
}
/**
* 获取网站分类的总数 (池中有多少个网站类型)
*
* @return int
*/
public int getWebSiteCount() {
return pool.size();
}
}
public static void main(String[] args) {
// 创建一个工厂类
WebSiteFactory factory = new WebSiteFactory();
// 客户要一个以新闻形式发布的网站
WebSite webSite1 = factory.getWebSiteCategory("新闻");
webSite1.use(new User("tom"));
// 客户要一个以博客形式发布的网站
WebSite webSite2 = factory.getWebSiteCategory("博客");
webSite2.use(new User("jack"));
// 客户要一个以博客形式发布的网站
WebSite webSite3 = factory.getWebSiteCategory("博客");
webSite3.use(new User("smith"));
System.out.println("网站的分类共=" + factory.getWebSiteCount());
}
享元模式在源码中的应用
Integer
public final class Integer extends Number implements Comparable<Integer> {
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
......
}