《JAVA与模式》之享元模式
享元的定义:
享元模式英文称为“Flyweight Pattern”,如果翻译成为羽量级模式,估计会让人有点捉摸不透,而享元,则可以较好的体现该模式的用途,共享元素
享元的用途:
我们知道,在java应用中,会出现许多String a="123",String b="123"之类的String类型的变量,如果只是小应用,到还好,假设是一个庞大的系统,
有好多处都需要用定义String a="223",那开销可想而知,而JDK的开发者自然想到了这点,采用了享元模式解决创建大量相同String变量带来的
开销问题
1 String s1 = "a"; 2 String s2 = "a"; 3 System.out.println(s1 == s2);// String 也是采用享元模式 存储在常量池
执行结果:true
到此,我们先简单总结下何种情况下可以考虑使用享元模式
1、一个系统中存在着大量的细粒度对象;
2、这些细粒度对象耗费了大量的内存。
3、这些细粒度对象的状态中的大部分都可以外部化
1 // 接口 2 interface FlyWeight { 3 public void operation(String s); 4 } 5 6 // 具体实现类 7 class ConcreteFlyWeight implements FlyWeight { 8 private String str;// 内蕴状态 9 10 public ConcreteFlyWeight(String str) { 11 12 this.str = str; 13 } 14 15 public void operation(String s) { 16 System.out.println("内蕴变量:" + str); 17 System.out.println("外蕴变量:" + s); 18 } 19 } 20 21 // 享元工厂 22 class FlyWeightFactory { 23 public FlyWeightFactory() { 24 } 25 26 private Hashtable<String, ConcreteFlyWeight> flyWeights = new Hashtable<String, ConcreteFlyWeight>(); 27 28 public ConcreteFlyWeight factory(String str) { 29 30 ConcreteFlyWeight flyWeight; 31 32 flyWeight = flyWeights.get(str); 33 34 if (null == flyWeight) { 35 flyWeight = new ConcreteFlyWeight(str); 36 flyWeights.put(str, flyWeight); 37 38 } 39 return flyWeight; 40 } 41 42 public int getFlyWeightSize() { 43 44 return flyWeights.size(); 45 } 46 }
执行代码
1 FlyWeightFactory factory = new FlyWeightFactory(); 2 FlyWeight flyWeight = factory.factory("a"); 3 FlyWeight flyWeight2 = factory.factory("b"); 4 FlyWeight flyWeight3 = factory.factory("a"); 5 flyWeight.operation("a fly weight"); 6 flyWeight2.operation("b fly weight"); 7 flyWeight3.operation("c fly weight"); 8 System.out.println(flyWeight == flyWeight3); 9 System.out.println(factory.getFlyWeightSize());
执行结果
内蕴变量:a
外蕴变量:a fly weight
内蕴变量:b
外蕴变量:b fly weight
内蕴变量:a
外蕴变量:c fly weight
true
2
结论:本例子享元模式中,享元工厂只有2个对象,外部可以共享他们,并且内蕴变量不会受到影响
整理下模式框架代码
● 抽象享元(Flyweight)角色 :给出一个抽象接口,以规定出所有具体享元角色需要实现的方法。
● 具体享元(ConcreteFlyweight)角色:实现抽象享元角色所规定出的接口。如果有内蕴状态的话,必须负责为内蕴状态提供存储空间。
● 享元工厂(FlyweightFactory)角色 :本角色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当地共享。当一个客户端对象调用一个享元对象的时候,享元工厂角色会检查系统中是否已经有一个符合要求的享元对象。如果已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,享元工厂角色就应当创建一个合适的享元对象。
我们会发现,享元模式其实是简单工厂的包装,当然实际场景是不一样的
复合享元模式
上述说的其实是单纯的享元,享元模式还有一种复杂的情况,就是复合享元。
在单纯享元模式中,所有的享元对象都是单纯享元对象,也就是说都是可以直接共享的
将一些单纯享元使用合成模式加以复合,形成复合享元对象。这样的复合享元对象本身不能共享,但是它们可以分解成单纯享元对象,而后者则可以共享。
我们看下个例子
1 CompositeFlyWeigthFactory factory2=new CompositeFlyWeigthFactory(); 2 List<String> ls=Arrays.asList(new String[]{"a","b","c","a","d"}); 3 FlyWeight compositeFly=factory2.factory(ls); 4 FlyWeight compositeFly2=factory2.factory(ls); 5 System.out.println("---------------compositeFly-----------------"); 6 compositeFly.operation("a composite fly weight"); 7 System.out.println("---------------compositeFly2-----------------"); 8 compositeFly2.operation("a composite fly weight"); 9 10 System.out.println(compositeFly==compositeFly2); 11 System.out.println(factory2.factory("a")==factory2.factory("a"));
执行结果:
内蕴变量:d
外蕴变量:a composite fly weight
内蕴变量:a
外蕴变量:a composite fly weight
内蕴变量:c
外蕴变量:a composite fly weight
内蕴变量:b
外蕴变量:a composite fly weight
false
true
整理下,会发现复合享元模式,发现是跟组合模式类似。与单纯享元模式比较,提供了一个add方法,用来组合多个单纯享元对象
整理下模式框架代码
● 抽象享元(Flyweight)角色 :给出一个抽象接口,以规定出所有具体享元角色需要实现的方法。
● 具体享元(ConcreteFlyweight)角色:实现抽象享元角色所规定出的接口。如果有内蕴状态的话,必须负责为内蕴状态提供存储空间。
● 复合享元(ConcreteCompositeFlyweight)角色 :复合享元角色所代表的对象是不可以共享的,但是一个复合享元对象可以分解成为多个本身是单纯享元对象的组合。复合享元角色又称作不可共享的享元对象。
● 享元工厂(FlyweightFactory)角色 :本角 色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当地共享。当一个客户端对象调用一个享元对象的时候,享元工厂角色会检查系统中是否已经有 一个符合要求的享元对象。如果已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,享元工厂角色就应当创建一个 合适的享元对象。
我们模拟一个情景,饭店的菜单只有一份,而每个顾客点的菜单却各不相同,但是肯定会有重复,我们用上述的享元模式尝试模拟下代码情景
1 //饭店菜谱工厂 2 CompositeFlyWeigthFactory orderFactory=new CompositeFlyWeigthFactory(); 3 //菜单列表 4 List<String> menuList=Arrays.asList(new String[]{"福跳墙","荔枝肉","锅边糊","油条","麻花"}); 5 FlyWeight c1 =orderFactory.factory(menuList.subList(0, 2));//顾客1 6 FlyWeight c2=orderFactory.factory(menuList.subList(1, 3));//顾客2 7 System.out.println("---------------c1-----------------"); 8 c1.operation("c1的菜单谱"); 9 System.out.println("---------------c2-----------------"); 10 c2.operation("c2的菜单普");
执行结果
---------------c1-----------------
内蕴变量:荔枝肉
外蕴变量:c1的菜单谱
内蕴变量:福跳墙
外蕴变量:c1的菜单谱
---------------c2-----------------
内蕴变量:锅边糊
外蕴变量:c2的菜单普
内蕴变量:荔枝肉
外蕴变量:c2的菜单普
参考:
http://blog.csdn.net/ai92/article/details/224598
http://blog.csdn.net/jason0539/article/details/22908915
http://www.cnblogs.com/java-my-life/archive/2012/04/26/2468499.html#2937200