享元模式
转载请注明出处。!!
http://blog.csdn.net/zhonghuan1992
全部配套代码均在github上:https://github.com/ZHONGHuanGit/DesignPattern
跟着ZHONGHuan学习设计模式
享元模式
依据GOF95,享元模式是对象的结构模式,享元模式以共享的方式高效的支持大量细粒度对象。在本篇中会慢慢将它展现给你,以下我们先来了解几个概念。
享元对象:是对象,可是在享元模式中。就称之为享元对象,由于它有以下的一些状态。
内部状态:存储在享元对象内部。而且不会随着环境的改变而改变。
想想啊。就是由于这个具有内部状态的享元对象,才可以被用来共享。(享元嘛。有共享的意思包括在里面,肯定和共享有关咯)
外部状态:外部嘛。和内部肯定是相反的。哪里相反呢?就是它会随着环境的改变而改变,既然这个状态会这样,那么它就不能拿来共享咯。由于在不同状态下,它呈现不一样,不好拿来共享嘛(变来变去的是吧)。
《Java与模式》书中,享元模式分为单纯享元模式和复合享元模式。以下我们先来看一下单纯享元模式。在单纯享元模式中。全部对象是能够拿来共享的。
看以下的结构图
看一下各部分的作用是什么。
各个角色
以下部分有些晦涩。能够草草看一下之后,看以下的实例,再回头看以下的各个角色。
抽象享元角色(Flyweight):此角色是全部的详细享元类的超类,为这些类规定出须要实现的公共接口或抽象类。那些须要外部状态(External State)的操作能够通过方法的參数传入。
抽象享元的接口使得享元变得可能,可是并不强制子类实行共享,因此并不是全部的享元对象都是能够共享的。
详细享元(ConcreteFlyweight)角色:实现抽象享元角色所规定的接口。假设有内部状态的话,必须负责为内部状态提供存储空间。享元对象的内部状态必须与对象所处的周围环境无关,从而使得享元对象能够在系统内共享。有时候详细享元角色又叫做单纯详细享元角色,由于复合享元角色是由单纯详细享元角色通过复合而成的。
享元工厂(FlyweightFactoiy)角色:本角色负责创建和管理享元角色。
本角色必须保证享元对象能够被系统适当地共享。当一个client对象请求一个享元对象的时候,享元工厂角色须要检查系统中是否已经有一个符合要求的享元对象,假设已经有了,享元工厂角色就应当提供这个已有的享元对象;假设系统中没有一个适当的享元对象的话,享元工厂角色就应当创建一个新的合适的享元对象。
client(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); } }
必须说明一下,client是不会初始化享元对象的,实例化的任务交给了FlyweightFactory来做。
当client须要享元对象的时候,调用享元工厂的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; }
说道这儿,应该对享元模式有了初步的了解了。那么我们何时使用它呢?事实上这个能够从它的作用出发。它是为了管理大量的细粒度对象的。而且这些对象是有内部状态能够反复拿来用的,由于大量的对象是非常占内存的,这个时候我们就能够使用享元模式。