当前位置: 首页 > 后端技术 > Java

DesignPatterns[10]--顺便看看FlyweightPattern

时间:2023-04-01 17:28:43 Java

设计模式系列:http://aphysia.cn/categories/...开篇还是同种图,各位嘉宾往下看...享元模式是什么?FlyWeight是一种结构模式,主要是为了减少创建对象的数量,减少内存占用,提高性能。说到这里,不知道大家会不会想到池技术,比如String常量池、数据库连接池、缓冲池等,没错,这些都是应用享元模式的。比如有一些对象需要大量资源来创建,创建成本比较高,内存开销比较大。如果我们一直创建,机器承受不住,那么我们就想到pooling技术,把创建出来的对象放到里面,去池子里拿就可以了,也就是大家共享池子里的对象,这就是共享。听名字就很容易共享单车:享元模式的特点。一般来说,享元对象需要在不同的场景下使用。如果状态可以随意修改,很容易造成混乱,大大增加出错的概率。但是,如果所有的内部属性都是不可修改的,就好像不是很灵活。因此,为了在稳定性和灵活性之间找到平衡,一般享元对象会将内部属性分为两类:内部状态:对于不可变、多处共享、复用的部分,外部状态只能通过constructor:每个对象在不同的??场景下可能有不同的状态,简单享元模式可以修改:在简单享元模式下,可以共享所有具体的享元类,没有非共享的具体享元类。复合享受方式:将一些简单的享受对象用组合方式组合起来,也可以形成复合享受对象。这样的复合享受对象本身是不能共享的,但是可以分解成简单的享受对象,后者可以共享这里说的是简单享元模式,一般有几个对象:享元接口或者抽象类(享元):public方法在接口或抽象类中声明和定义,可以对外提供一些能力。或者按需提供数据。具体享元实现类(ConcreteFlyweight):实现抽象享元类,部分内部数据不可变。接口实现的时候,会对外提供一些能力或者数据。FlyweightFactory:FlyweightFactory主要用来创建和管理享元对象,把各种类型的享元对象放到一个池子里,一般是键值对的形式,当然也可以是其他的如果你是第一次拿到一个对象,您需要先创建它。如果对象已经存在于池中,可以直接返回。比如我们出去玩需要买机票。假设航班的唯一性与航班号、起飞时间和到达时间有关。用户喜欢通过航班号查询航班信息。首先,我们需要为航班创建一个接口:publicinterfaceIFlight{voidinfo();}私人字符串开始;私有字符串结束;私有布尔值延迟;publicFlight(StringflightNo,Stringstart,Stringend){this.flightNo=flightNo;this.start=开始;this.end=结束;isDelay=Math.random()>0.5;}@Overridepublicvoidinfo(){System.out.println(String.format("Flight[%s]from[%s]to[%s]:%s",start,end,flightNo,isDelay?"延迟department":"正常出发"));}}航班搜索工厂ClassFlightSearchFactory:publicclassFlightSearchFactory{publicstaticIFlightsearchFlight(StringflightNo,Stringstart,Stringend){returnnewFlight(flightNo,start,end);}}模拟客户端请求:publicclassClientTest{publicstaticvoidmain(String[]args){IFlightflight=FlightSearchFactory.searchFlight("C9876","北京","上海");航班信息();}}我们可以看到打印出如下信息:Theflightfrom[Beijing]to[Shanghai][C9876]:delayed但是上面的有问题。每次访问时,都会创建一个对象。乘坐同一航班的人理论上应该查询相同的数据。这部分其实是可以共享和复用的,提高效率。为什么不这样做呢?如何缓存它?我们一般使用HashMap来缓存,只需要定义唯一标识的key即可:;publicstaticIFlightsearchFlight(StringflightNo,Stringstart,Stringend){Stringkey=getKey(flightNo,start,end);IFlightflight=maps.get(key);if(flight==null){System.out.print("不在缓存中,需要重建:");航班=新航班(航班号,开始,结束);maps.put(钥匙,航班);}else{System.out.print("从缓存中读取数据:");}回程飞机;}privatestaticStringgetKey(StringflightNo,Stringstart,Stringend){returnString.format("%s_%s_%s",flightNo,start,end);}}测试代码:publicclassClientTest{publicstaticvoidmain(String[]args){IFlightflight=FlightSearchFactory.searchFlight("C9876","北京","上海");航班信息();IFlightflight1=FlightSearchFactory.searchFlight("C9876","北京","上海");flight1.info();IFlightflight2=FlightSearchFactory.searchFlight("H1213","北京","广州");flight2.info();}}测试结果:缓存无,需要重建:[北京]至[上海]航班[C9876]:正常起飞从缓存中读取数据:[北京]至[上海]航班[C9876]:正常起飞不在cache,Needtorebuild:flightfrom[Beijing]to[Guangzhou][H1213]:正常起飞,可以看到如果cache中有,那么这个对象就不会重建,共享对象的目的可以取得成就。我们在项目中通常会用到各种连接池,比如Redis连接池,Mysql连接池等,这些资源本来就很珍贵,我们可以共享JDK中的Integer其实是使用缓存技术的,因为大家普遍使用比较小的值,所以默认情况下,如果使用valuesOf(inti)方法获取Integer,会先读取缓存内容:publicstaticIntegervalueOf(inti){if(i>=IntegerCache.low&&i<=IntegerCache.high)returnIntegerCache.cache[i+(-IntegerCache.low)];返回新整数(i);我们可以看到如果在low和high范围内的数据会从缓存中获取,否则会直接新建一个对象,那么low和high的范围是多少呢?staticfinalintlow=-128;静态最终int高;high是动态的,但是high是断言的,必须大于等于127:assertIntegerCache.high>=127;,范围可以来自java.lang.Integer。配置项IntegerCache.high读作:static{//high值可以通过属性配置inth=127;StringintegerCacheHighPropValue=sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");if(integerCacheHighPropValue!=null){try{inti=parseInt(integerCacheHighPropValue);i=Math.max(i,127);//最大数组大小为Integer.MAX_VALUEh=Math.min(i,Integer.MAX_VALUE-(-low)-1);}catch(NumberFormatExceptionnfe){//如果属性无法解析为int,则忽略它。}}高=h;cache=newInteger[(high-low)+1];intj=低;for(intk=0;k=127;}测试下:publicclassIntegerTest{publicstaticvoidmain(String[]args){//不同Integerinteger=Integer.valueOf(128);整数integer1=Integer.valueOf(128);System.out.println(整数==integer1);//相等Integerinteger2=Integer.valueOf(127);整数integer3=Integer.valueOf(127);System.out.println(输入teger2==integer3);//等于Integerinteger4=Integer.valueOf(0);整数integer5=Integer.valueOf(0);System.out.println(integer4==integer5);//等于Integerinteger6=Integer.valueOf(-128);整数integer7=Integer.valueOf(-128);System.out.println(integer6==integer7);//不等于Integerinteger8=Integer.valueOf(-129);整数integer9=Integer.valueOf(-129);System.out.println(integer8==integer9);}}从上面的结果可以看出,Integer其实是从-128到127缓存的,我们的结果也验证了。注意Integer.valueOf必须使用()这个方法,如果使用构造函数newInteger(),必须是新创建的对象,经过特殊处理,代码复杂度增加。设计模式其实是在软件工程的不断探索中总结出来的一种常用的设计思想。它不是必须使用的,也不是灵丹妙药,但总有值得学习的东西,了解其设计的好处,并不断改进我们编写的代码,即使我们每次都改进一点点。曾经听过一句话:看到别人写得不优雅的代码,就有重构的冲动。你可以多读你写的代码,然后写得更好(大致就是这个意思)。共勉!【作者简介】:公众号【秦淮杂货铺】作者秦淮,个人网站:http://aphysia.cn,技术之路非一蹴而就,山高水长江河漫漫,纵然缓慢,也不会停下脚步。剑指全题OfferPDF开源编程笔记

最新推荐
猜你喜欢