享元模式浅析
Posted xiao-lu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了享元模式浅析相关的知识,希望对你有一定的参考价值。
源码面前,了无秘密。
1 package com.xiaolu.flyweightdemotest; 2 3 4 import org.junit.Assert; 5 import org.junit.Test; 6 7 import com.xiaolu.flyweight.FlyweigthtFactory; 8 import com.xiaolu.flyweight.IChess; 9 10 11 12 /*** 13 * 享元模式:flyweight有轻量的意思。享元即共享。这里的轻量到底指什么? 14 * 15 * 在享元模式中分为内蕴状态和外蕴状态 16 * 其中: 17 * 1) 内蕴状态是不随环境而改变的,因此可以用用于共享。 (而一个对象中的内蕴状态很少,flyweight更多的表明了这个模式的用途---轻量级对象的共享) 18 * 2) 外蕴状态是随着环境的变化而变化的。外蕴状态一般是由外部传入的。 19 * 20 * 享元模式定义: 所谓享元模式就是运行共享技术有效地支持大量细粒度对象的复用。 ps: 细粒度(其实也就是因为内蕴状态少,因此对象"小"----可以从内存的观点去考虑) 21 * 使用享元模式的场景是: 当系统需要大量创建对象时,如果能够使用享元模式可以大量节省内存,提高效率。 22 * 23 * 在这里思考一个问题:为什么是细粒度的? 也就是为什么是"小"对象(内蕴状态很少的)对象进行共享呢? 如果多了会如何,内蕴状态多了进行共享不是更能节省内存,享元模式的意义更大吗? 24 * 所谓能够共享,即多个对象内部的状态应当是一样的,否则就谈不上共享。那么如果一个对象的内蕴状态越多,能共享的可能性就越低。【如果有一个对象只有一个属性和有10个属性的对象,哪个更 25 * 容易共享呢? 如果A、B、C三个客户想要共享,那么如果有10个内蕴状态的,就需要10个属性都一致,A、B、C三个客户才可以共享。而 一个属性的则就方便多了。 当然多个属性的也并非不能共享, 26 * 但是从现实场景考虑-----还是算了】 27 * 28 * 但是还有一个问题: 是不是属性越少的就可以去使用享元模式进行共享? 29 * 不见得。那怕只有一个属性,但是如果属性的取值范围很大(甚至是任意的),那么享元模式基本上就没什么意义(每次都是创建新的) 30 * 31 * java中的String类型即使用了享元模式 "abc" == "abc" , 两个相同字符串共享同一个String对象。 ps:但是因为字符串相等,包括长度和顺序,因此如果一个程序中 32 * 没有任何两个字符串是相等的(equals),那么显然也就不能"共享"了。当然从大的范围看,还是有很多情况下,相同字符串多次出现的(否则sun也不傻......) 33 * 34 * 35 * 我觉得经典使用享元模式的场景是: 五子棋 (围棋) , 英文字符的书写。 36 * 五子棋,不管有多少个,都是只有黑白两色。只是分布在不同的位置。因此就可以使用享元模式(白色棋子、黑色棋子共享),而放置的位置信息则可以通过外蕴状态来输入。 37 * 英文字符。26*2 = 52个(大小写),其他的位置、颜色、粗细等,可以通过外部来传入。(否则一篇文章有多长,需要创建多少个对象?一本书呢?哦.....) 38 * 39 * 40 * 还有复合享元,下次有时间再看。 41 * 42 * @author xiaolu 2018年10月11日 43 */ 44 public class TestFlyweight { 45 @Test 46 public void test_StringFlyweight() throws Exception { 47 String a = "xiaolu"; 48 String b = "xiaolu"; 49 Assert.assertTrue(a == b); 50 } 51 52 53 @Test 54 public void test_flyweightdemo() throws Exception { 55 IChess black = FlyweigthtFactory.INSTANCE.get("black");// 黑色棋子 56 IChess white = FlyweigthtFactory.INSTANCE.get("white");//白色棋子 57 // 外蕴状态即是位置,通过外部参数传入 58 black.put(10, 10); 59 white.put(100, 100); 60 black.put(12, 12); 61 white.put(30, 30); 62 black.put(15 , 15 ); 63 white.put(100, 100); 64 65 // 因为有工厂的存在不用过多思考,一共创建了2个对象。【就是把black换成FlyweigthtFactory.INSTANCE.get("black")】也没什么区别,除了写起来麻烦。 66 } 67 68 69 }
1 package com.xiaolu.flyweight; 2 3 /** 4 * 享元接口 此接口往往定义了享元对象的提供的行为,将外蕴状态通过行为的参数来传入 5 * @author xiaolu 2018年10月11日 6 */ 7 public interface IChess { 8 9 /*** 10 * 假设的放置棋子的方法 x, y 分别代表坐标 11 * @param x 12 * @param y 13 */ 14 public void put(int x , int y); 15 }
1 package com.xiaolu.flyweight; 2 3 public class Chess implements IChess { 4 private String color; 5 6 /** 7 * 网上资料将此处传入的常常称为"外蕴状态",个人觉得更应该叫做“内蕴状态”或者 "内蕴状态的对应者" 8 * 没有这种对应,无法知道你想要"共享什么内蕴"的对象。 9 * 10 * @author : xiaolu 2018年10月11日 11 */ 12 public Chess(String color) { 13 this.color = color; 14 } 15 16 @Override 17 public void put(int x, int y) { 18 System.out.println(this.color + " 棋 放在 x=" + x + ", y=" + y + "的位置."); 19 } 20 21 }
1 package com.xiaolu.flyweight; 2 3 import java.util.HashMap; 4 import java.util.Map; 5 6 /** 7 * 享元工厂,采用单例实现(此处通过enum来实现,线程安全,规避反序列化-----新学到东西的一次实践) 8 * ps:莫名就想起了cache---------------- 9 * @author xiaolu 2018年10月11日 10 */ 11 public enum FlyweigthtFactory { 12 INSTANCE; 13 14 private Map<String, IChess> chesses = new HashMap<String, IChess>(2); 15 16 private FlyweigthtFactory() { 17 chesses.put("black", new Chess("black")); 18 chesses.put("white", new Chess("white")); 19 } 20 21 /** 22 * 此处有一个有趣的地方:应该使用assert 呢还是,if判断抛异常? 23 * 使用assert ,assert在调试时,打开jre -ea即可起作用,而在产品中则一般不会开启-ea,从而省去了代码检查。关键点在于: 24 * 用户的输入点是需要我们去进行判断的(因为不知道从界面上获取的用户输入会如何-----思维跳出当前这个五子棋吧 , ,想想java-web开发,应该在controller就进行输入验证)。而此处逻辑是我们程序 25 * 自己内部调用的,如果此处去检查抛出异常,那么就是前面没有进行错误检查处理。属于程序逻辑设计问题了。 26 * 在开发时内部使用assert,一旦测试通过,则可以忽略掉。毕竟层层检查有时候是无意义并且浪费效率的。(此处也是借鉴了《大道至简》上的思维) 27 * @param color 28 * @return 29 */ 30 public IChess get(String color) { 31 assert chesses.containsKey(color); 32 33 /* 34 * 一般是如此实现----(cache的方式),由于五子棋,我们知道就是黑白两色,因此在构造函数中直接构造,也就不需要此处再去判断加入了。若范围不确定,则需要(就是一般cache的实现思维) 35 * 36 * if(!chesses.containsKey(color)){ 37 * chesses.put(color,new Chess(color)); 38 * } 39 */ 40 return chesses.get(color); 41 } 42 }
以上是关于享元模式浅析的主要内容,如果未能解决你的问题,请参考以下文章