转载请注明出处!!!http://blog.csdn.net/zhonghuan1992
所有配套代码均在github上:https://github.com/ZHONGHuanGit/DesignPattern
跟着ZHONGHuan学习设计模式
享元模式
根据GOF95,享元模式是对象的结构模式,享元模式以共享的方式高效的支持大量细粒度对象。在本篇中会慢慢将它展现给你,下面我们先来了解几个概念。
享元对象:是对象,但是在享元模式中,就称之为享元对象,因为它有下面的一些状态。
内部状态:存储在享元对象内部,并且不会随着环境的改变而改变。想想啊,就是因为这个具有内部状态的享元对象,才能够被用来共享。(享元嘛,有共享的意思包含在里面,肯定和共享有关咯)
外部状态:外部嘛,和内部肯定是相反的,哪里相反呢?就是它会随着环境的改变而改变,既然这个状态会这样,那么它就不能拿来共享咯,因为在不同状态下,它呈现不一样,不好拿来共享嘛(变来变去的是吧)。
《Java与模式》书中,享元模式分为单纯享元模式和复合享元模式。下面我们先来看一下单纯享元模式。在单纯享元模式中,所有对象是可以拿来共享的。看下面的结构图
看一下各部分的作用是什么。
各个角色
以下部分有些晦涩,可以草草看一下之后,看下面的实例,再回头看下面的各个角色。
抽象享元角色(Flyweight):此角色是所有的具体享元类的超类,为这些类规定出需要实现的公共接口或抽象类。那些需要外部状态(External State)的操作可以通过方法的参数传入。抽象享元的接口使得享元变得可能,但是并不强制子类实行共享,因此并非所有的享元对象都是可以共享的。
具体享元(ConcreteFlyweight)角色:实现抽象享元角色所规定的接口。如果有内部状态的话,必须负责为内部状态提供存储空间。享元对象的内部状态必须与对象所处的周围环境无关,从而使得享元对象可以在系统内共享。有时候具体享元角色又叫做单纯具体享元角色,因为复合享元角色是由单纯具体享元角色通过复合而成的。
享元工厂(FlyweightFactoiy)角色:本角色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当地共享。当一个客户端对象请求一个享元对象的时候,享元工厂角色需要检查系统中是否已经有一个符合要求的享元对象,如果已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,享元工厂角色就应当创建一个新的合适的享元对象。
客户端(Client)角色:本角色还需要自行存储所有享元对象的外部状态。
实例引入:
上面的东西太晦涩,干巴巴的太结实了,不好懂,让我们从实例来学习。我们假设这样一个场景,我现在在使用word,我们以文字来举例(可以包括中文,字母)。每个文字或字母,其实都有内部状态和外部状态。想想看,一个字就是它的内部状态,但是这个字的字体,大小等是外部状态,而且汉字不少,所以为了便于管理和使用,可以使用享元模式。享元模式不是支持大量细粒度对象吗?这些字就是细粒度的。
下面让我们来实现它,先看看抽象的享元角色。
//抽象享元角色
abstract class Flyweight{
abstract public void show(String state);//声明了一个show方法,这里用来展示字母
}
下面看一下实在的享元对象,它是有内部状态的,这里是一个Character类型的字母来表示,它的外部状态假定为字体,所以穿进去一个String。看一下它的实现。
class ConcreteFlyweight extends Flyweight {
private Character intrinsicState = null;// 内部状态
// 构造函数,内部状态作为参数穿进去
public ConcreteFlyweight(Character state) {
intrinsicState = state;
}
// 根据外部状态来展示
public void show(String state) {
System.out.println("我是 " + intrinsicState + " 当前字体为:" + state);
}
}
必须说明一下,客户端是不会初始化享元对象的,实例化的任务交给了FlyweightFactory来做。当客户端需要享元对象的时候,调用享元工厂的factory()就可以了。下面看一下享元工厂的实现。
class FlyweightFactory {
private HashMap<Character,Flyweight> map = new HashMap<Character,Flyweight>(); // 用来存储享元对象
public FlyweightFactory() {
}
// 创建享元对象的方法,内部状态作为参数输入
public Flyweight factory(Character state) {
if (map.containsKey(state)) {
return map.get(state);
} else {
Flyweight ft = new ConcreteFlyweight(state);
map.put(state, ft);
return ft;
}
}
// 遍历享元
public void checkFlyweight(){
System.out.println("=========checkFlyweight==================");
int i=0;
for(Iterator it = map.entrySet().iterator();it.hasNext();i++){
Map.Entry e = (Map.Entry)it.next();
System.out.println("第"+i+"项是:"+e.getKey());
}
System.out.println("=========checkFlyweight==================");
}
}
看Client的部分
public class Client {
FlyweightFactory factory = new FlyweightFactory();
void run(){
Flyweight fly = factory.factory(new Character('a'));
fly.show("宋体");// 这个a是宋体的格式
fly = factory.factory(new Character('b'));
fly.show("xx体");// 这个b是xx体的格式
fly = factory.factory(new Character('a'));
fly.show("宋体");// 这个a是宋体的格式
factory.checkFlyweight();
}
public static void main(String[] args) {
new Client().run();
}
}
运行是结果如下:
现在你再回头看一下各部分角色,你就会有更清楚的认识了。
下面看以下复合享元模式,其实就是在之前的基础上,增加了一个复合享元对象。
新增的角色:
复合享元(UnsharableFlyweight)角色:复合享元角色所代表的对象是不可以共享的,但是一个复合享元对象可以分解成为多个本身是单纯享元对象的组合。复合享元角色又称做不可共享的享元对象。这个角色一般很少使用。
下面看复合享元对象实现代码。
//复合享元对象
class CompositeFlyweight extends Flyweight {
// 用来存储享元对象,因为复合享元对象是通过普通的享元对象复合而来的。
private HashMap<Character,Flyweight> map = new HashMap<Character,Flyweight>();
// 构造函数,
public CompositeFlyweight() {
}
//增加一个新的单纯享元对象到聚集中
public void add(Character key,Flyweight fly){
map.put(key,fly);
}
// 根据外部状态来展示
public void show(String state) {
int i=0;
for(Iterator it = map.entrySet().iterator();it.hasNext();i++){
Map.Entry e = (Map.Entry)it.next();
System.out.println("第"+i+"项是:"+e.getKey()+" 字体是:"+state);//暂时假定在一块的字体一样
}
}
}
当然,享元工厂也要提供复合享元对象的实例化方式,因为上面的例子中使用的是Character来标记享元对象,所以可以用String来实例化复合享元对象。所以在factory中加入一个方法,用来实例化复合享元对象。
public Flyweight factory(String state){
CompositeFlyweight cfly = new CompositeFlyweight();
int len = state.length();
Character c = null;
for(int i=0;i<len;i++){
c = new Character(state.charAt(i));
cfly.add(c,this.factory(c));
}
return cfly;
}
说道这儿,应该对享元模式有了初步的了解了。那么我们何时使用它呢?其实这个可以从它的作用出发,它是为了管理大量的细粒度对象的,并且这些对象是有内部状态可以重复拿来用的,因为大量的对象是很占内存的,这个时候我们就可以使用享元模式。