项目多也别傻做——享元模式

项目多也别傻做——享元模式

项目多也别傻做!

时间:7月9日21点  地点:小菜、大鸟住所的客厅  人物:小菜、大鸟

"小菜,最近一直在忙些什么呢?回家就自个忙开了。"大鸟问道。

“哦,最近有朋友介绍我一些小型的外包项目,是给一些私营业主做网站,我想也不是太难的事情,对自己也是一个很好的编程锻炼,所以最近我都在开发当中。”

“哈,看来小菜有外快赚了,又能锻炼自己的技术,好事呀。”

“现实不是想象得这么简单。刚开始是为一个客户做一个产品展示的网站,我花了一个多星期的时间做好了,也帮他租用了虚拟空间,应该说都很顺利。”

“嗯,产品展示网站,这个应该不难实现。”

“而后,他另外的朋友也希望能做这样的网站,我想这有何难,再租用一个空间,然后把之前的代码复制一份上传,就可以了。”

“哦,这好像有点问题,后来呢?”

“实际上却是他们的朋友都希望我来提供这样的网站,但要求就不太一样了,有的人希望是新闻发布形式的,有的人希望是博客形式的,也有还是原来的产品图片加说明形式的,而且他们都希望在费用上能大大降低。可是每个网站租用一个空间,费用上降低是不太可能的。我在想如何办呢?”

“他们是不是都是类似的商家客户?要求也就是信息发布、产品展示、博客留言、论坛等功能?”

"是呀,要求差别不大。你说该如何办?"小菜问道。

“你的担心是对的,如果有100家企业来找你做网站,你难道去申请100个空间,用100个数据库,然后用类似的代码复制100遍,去实现吗?”

网站类:

在这里插入图片描述

“啊,那如果有Bug或是新的需求改动,维护量就太可怕了。”

“先来看看你现在的做法。如果是每个网站一个实例,代码应该是这样的。”

在这里插入图片描述

结果显示:

在这里插入图片描述

“对的,也就是说,如果要做三个产品展示,三个博客的网站,就需要六个网站类的实例,而其实它们本质上都是一样的代码,如果网站增多,实例也就随着增多,这对服务器的资源浪费得很严重。小菜,你说有什么办法解决这个问题?”

“我不知道,我想过大家的网站共用一套代码,但毕竟是不同的网站,数据都不相同的。”

“我就希望你说出共享代码这句话,为什么不可以呢?比如现在大型的博客网站、电子商务网站,里面每一个博客或商家也可以理解为一个小的网站,但它们是如何做的?”

“啊,我明白了,利用用户ID的不同,来区分不同的用户,具体数据和模板可以不同,但代码核心和数据库却是共享的。”

“小菜又开窍了,项目多也别傻做呀。你想,首先你的这些企业客户,他们需要的网站结构相似度很高,而且都不是那种高访问量的网站,如果分成多个虚拟空间来处理,相当于一个相同网站的实例对象很多,这造成服务器的大量资源浪费,当然更实际的其实就是钞票的浪费,如果整合到一个网站中,共享其相关的代码和数据,那么对于硬盘、内存、CPU、数据库空间等服务器资源都可以达成共享,减少服务器资源,而对于代码,由于是一份实例,维护和扩展都更加容易。”

“是的。那如何做到共享一份实例呢?”

享元模式

“哈,在弄明白如何共享代码之前,我们先来谈谈一个设计模式——享元模式。”

享元模式(Flyweight),运用共享技术有效地支持大量细粒度的对象。[DP]

在这里插入图片描述

Flyweight类,是所有具体享元类的超类或接口,通过这个接口,Flyweight可以接受并作用于外部状态。

在这里插入图片描述

UnsharedConcreteFlyweight是指那些不需要共享的Flyweight子类。因为Flyweight接口共享成为可能,但它并不强制共享。

在这里插入图片描述

FlyweightFactory是一个享元工厂,用来创建并管理Flyweight对象。它主要是用来确保合理地共享Flyweight,当用户请求一个Flyweight时,FlyweightFactory对象提供一个已创建的实例或者创建一个(如果不存在的话)。

在这里插入图片描述

结果显示:

在这里插入图片描述

"大鸟,有个问题,"小菜问道,“FlyweightFactory根据客户需求返回早已生成好的对象,但一定要事先生成对象实例吗?”

“问得好,实际上是不一定需要的,完全可以初始化时什么也不做,到需要时,再去判断对象是否为null来决定是否实例化。”

“还有个问题,为什么要有UnsharedConcreteFlyweight的存在呢?”

“这是因为尽管我们大部分时间都需要共享对象来降低内存的损耗,但个别时候也有可能不需要共享,那么此时的UnsharedConcreteFlyweight子类就有存在的必要了,它可以解决那些不需要共享对象的问题。”

网站共享代码

"好了,你试着参照这个样例来改写一下帮人做网站的代码。"大鸟接着说。

“哦,好的,那这样的话,网站应该有一个抽象类和一个具体网站类才可以,然后通过网站工厂来产生对象。我马上就去写。”

半小时后,小菜的第二版代码。

网站抽象类:

在这里插入图片描述

具体网站类:

在这里插入图片描述

客户端代码:

在这里插入图片描述

结果显示:

在这里插入图片描述

“这样写算是基本实现了享元模式的共享对象的目的,也就是说,不管建几个网站,只要是’产品展示’,都是一样的,只要是’博客’,也是完全相同的,但这样是有问题的,你给企业建的网站不是一家企业的,它们的数据不会相同,所以至少它们都应该有不同的账号,你怎么办?”

“啊,对的,实际上我这样写没有体现对象间的不同,只体现了它们共享的部分。”

内部状态与外部状态

“在享元对象内部并且不会随环境改变而改变的共享部分,可以称为享元对象的内部状态,而随环境改变而改变的、不可以共享的状态就是外部状态了。事实上,享元模式可以避免大量非常相似类的开销。在程序设计中,有时需要生成大量细粒度的类实例来表示数据。如果能发现这些实例除了几个参数外基本上都是相同的,有时就能够大幅度地减少需要实例化的类的数量。如果能把那些参数移到类实例的外面,在方法调用时将它们传递进来,就可以通过共享大幅度地减少单个实例的数目。也就是说,享元模式Flyweight执行时所需的状态有内部的也可能有外部的,内部状态存储于ConcreteFlyweight对象之中,而外部对象则应该考虑由客户端对象存储或计算,当调用Flyweight对象的操作时,将该状态传递给它。”

“那你的意思是说,客户的账号就是外部状态,应该由专门的对象来处理。”

“来,你试试看。”

大约二十分钟后,小菜写出代码第三版。

在这里插入图片描述

用户类,用于网站的客户账号,是"网站"类的外部状态。

在这里插入图片描述

网站工厂类:

在这里插入图片描述

结果显示,尽管给六个不同用户使用网站,但实际上只有两个网站实例。

在这里插入图片描述

“哈,写得非常好,这样就可以协调内部与外部状态了。由于用了享元模式,哪怕你接手了1000个网站的需求,只要要求相同或类似,你的实际开发代码也就是分类的那几种,对于服务器来说,占用的硬盘空间、内存、CPU资源都是非常少的,这确实是很好的一个方式。”

享元模式应用

“大鸟,你通过这个例子来讲解享元模式虽然我是理解了,但在现实中什么时候才应该考虑使用享元模式呢?”

“就知道你会问这样的问题,如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大的存储开销时就应该考虑使用;还有就是对象的大多数状态可以是外部状态,如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象,此时可以考虑使用享元模式。

“在实际使用中,享元模式到底能达到什么效果呢?”

“因为用了享元模式,所以有了共享对象,实例总数就大大减少了,如果共享的对象越多,存储节约也就越多,节约量随着共享状态的增多而增大。”

“能具体一些吗?有些什么情况是用到享元模式的?”

“哈,实际上在Java中,字符串String就是运用了Flyweight模式。举个例子吧。'=='可以用来确定titleA与titleB是否是相同的实例,返回值为boolean值。当用new String()方法时,两个对象titleA和titleB的引用地址是不相同的,但当titleC和titleD都使用赋值的方式时,两个字符串的引用地址竟然是相同的。”

在这里插入图片描述

在这里插入图片描述

“啊,返回值竟然是True,titleC和titleD这两个字符串是相同的实例。”

“试想一下,如果每次创建字符串对象时,都需要创建一个新的字符串对象的话,内存的开销会很大。所以如果第一次创建了字符串对象titleC,下次再创建相同的字符串titleD时只是把它的引用指向’大话设计模式’,这样就实现了’大话设计模式’在内存中的共享。”

“哦,原来我一直在使用享元模式呀,我以前都不知道。还有没有其他现实中的应用呢?”

“虽说享元模式更多的时候是一种底层的设计模式,但现实中也是有应用的。比如说休闲游戏开发中,像围棋、五子棋、跳棋等,它们都有大量的棋子对象,你分析一下,它们的内部状态和外部状态各是什么?”

“围棋和五子棋只有黑白两色、跳棋颜色略多一些,但也是不太变化的,所以颜色应该是棋子的内部状态,而各个棋子之间的差别主要就是位置的不同,所以方位坐标应该是棋子的外部状态。”

“对的,像围棋,一盘棋理论上有361个空位可以放棋子,那如果用常规的面向对象方式编程,每盘棋都可能有两三百个棋子对象产生,一台服务器就很难支持更多的玩家玩围棋游戏了,毕竟内存空间还是有限的。如果用了享元模式来处理棋子,那么棋子对象可以减少到只有两个实例,结果……你应该明白的。”

在这里插入图片描述

“太了不起了,这的确是非常好地解决了对象的开销问题。”

“在某些情况下,对象的数量可能会太多,从而导致了运行时的资源与性能损耗。那么我们如何去避免大量细粒度的对象,同时又不影响客户程序,是一个值得去思考的问题,享元模式,可以运用共享技术有效地支持大量细粒度的对象。不过,你也别高兴得太早,使用享元模式需要维护一个记录了系统已有的所有享元的列表,而这本身需要耗费资源,另外享元模式使得系统更加复杂。为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化。因此,应当在有足够多的对象实例可供共享时才值得使用享元模式。”

“哦,明白了,像我给人家做网站,如果就两三个人的个人博客,其实是没有必要考虑太多的。但如果是要开发一个可供多人注册的博客网站,那么用共享代码的方式是一个非常好的选择。”

“小菜,说了这么多,你网站赚到钱了是不是该报答一下呀?”

“哈,如果开发完成后客户非常满意,我一定……我一定……”

“一定什么?怎么这么不爽快。”

“我一定送你一个博客账号!”

“啊!!!”

如果对你有帮助,就一键三连呗(关注+点赞+收藏),我会持续更新更多干货~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿陌名!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值