享元模式
动机:当对象数量太多时,将导致运行代价过高,带来性能下降等问题。
运用共享技术来有效地支持大量细粒度对象(较小的对象,它所包含的内部状态较少)的复用。是一种 *对象结构型模式*
共享通⽤对象,减少内存的使⽤,提升系统的访问效率。
内部状态和外部状态。
- 内部状态(Intrinsic State)指对象共享出来的信息,存储在享元信息内部,并且不会随环境的改变而改变;
- 外部状态(Extrinsic State)指对象得以依赖的一个标记,随环境的改变而改变,不可共享。
外部状态和内部状态是相互独立的,外部状态的变化不会引起内部状态的变化。由于区分了内部状态和外部状态,因此可以通过设置不同的外部状态使得相同的对象可以具有一些不同的特征,而相同的内部状态是可以共享的。
享元模式的本质是分离与共享 : 分离变与不变,并且共享不变。
抽象享元角色类
//所有具体享元类的超类或接口,通过这个接口,Flyweight可以接受并作用于外部状态。
public interface Flyweight {
//一个示意性方法,参数state是外部状态
public void operation(UnsharedConcreteFlyweight state);
}
非享元角色类
public class UnsharedConcreteFlyweight {
private String info;
UnsharedConcreteFlyweight(String info) {
this.info = info;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
}
具体享元角色类
public class ConcreteFlyweight implements Flyweight {
private String key;
// 具体享元角色类ConcreteFlyweight有一个内部状态,在本例中一个String类型的key属性代表.
// //被创建时赋予。所有的内部状态在对象创建之后,就不会再改变了。
/**
* 构造函数,内部状态作为参数传入
* @param state
*/
public ConcreteFlyweight(String key) {
this.key = key;
System.out.println("具体享元" + key + "被创建!");
}
// operation()方法的参数state是由外部传入的外部状态。外部状态都必须存储在客户端.
/**
* 外部状态作为参数传入方法中,改变方法的行为
* 但是并不改变对象的内部状态。
*/
public void operation(UnsharedConcreteFlyweight outState) {
System.out.print("具体享元" + key + "被调用,");
System.out.println("非享元信息是:" + outState.getInfo());
}
}
享元工厂角色类
// 客户端不可以直接将具体享元类实例化,而必须通过一个工厂对象,利用一个getFlyweight()方法得到享元对象.
//一般而言,享元工厂对象在整个系统中只有一个,因此也可以使用单例模式。
public class FlyweightFactory {
//定义一个池容器flyweights
private HashMap<String, Flyweight> flyweights = new HashMap<String, Flyweight>();
// 工厂创建新对象,返回Flyweight类对象
public Flyweight getFlyweight(String key) {
//先从缓存中查找对象
Flyweight flyweight = (Flyweight) flyweights.get(key);
if (flyweight != null) {
System.out.println("具体享元" + key + "已经存在,被成功获取!");
} else {
//如果对象不存在则创建一个新的Flyweight对象
flyweight = new ConcreteFlyweight(key);
//把这个新的Flyweight对象添加到缓存中
flyweights.put(key, flyweight);
}
return flyweight;
}
}
客户端类
public class FlyweightPattern {
public static void main(String[] args) {
FlyweightFactory factory = new FlyweightFactory();
Flyweight f01 = factory.getFlyweight("a");
Flyweight f02 = factory.getFlyweight("a");
Flyweight f11 = factory.getFlyweight("b");
Flyweight f03 = factory.getFlyweight("a");
Flyweight f12 = factory.getFlyweight("b");
f01.operation(new UnsharedConcreteFlyweight("第1次调用a。"));
f02.operation(new UnsharedConcreteFlyweight("第2次调用a。"));
f03.operation(new UnsharedConcreteFlyweight("第3次调用a。"));
f11.operation(new UnsharedConcreteFlyweight("第1次调用b。"));
f12.operation(new UnsharedConcreteFlyweight("第2次调用b。"));
}
}
程序运行结果如下:
具体享元a被创建!
具体享元a已经存在,被成功获取!
具体享元b被创建!
具体享元a已经存在,被成功获取!
具体享元b已经存在,被成功获取!
具体享元a被调用,非享元信息是:第1次调用a。
具体享元a被调用,非享元信息是:第2次调用a。
具体享元a被调用,非享元信息是:第3次调用a。
具体享元b被调用,非享元信息是:第1次调用b。
具体享元b被调用,非享元信息是:第2次调用b。
总结:在客户端进行创建享元工厂,进行创建享元对象,放入缓冲池中,如果缓冲池有享元对象则不创建对象。工厂创建完对象之后返回类型为Flyweight的对象。在客户端输入外部状态信息(也可以不存在),通过抽象对象Flyweight的实现类ConcreteFlyweight(多态),获取内部信息和外部信息。
享元模式包含四个角色:
- 抽象享元类声明一个接口,通过它可以接受并作用于外部状态;
- 具体享元类实现了抽象享元接口,其实例称为享元对象;
- 非共享具体享元是不能被共享的抽象享元类;
- 享元工厂类用于创建并管理享元对象,它针对抽象享元类编程,将各种类型的具体享元对象存储在一个享元池中。
享元模式以共享的方式高效地支持大量的细粒度对象,享元对象能做到共享的关键是区分内部状态和外部状态。其中内部状态是存储在享元对象内部并且不会随环境改变而改变的状态,因此内部状态可以共享;外部状态是随环境改变而改变的、不可以共享的状态。
引用: