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

工作中常用的设计模式——享元模式

时间:2023-04-01 20:26:36 Java

一般用于业务开发,不容易有大量使用设计模式的场景。这里总结一下业务开发中比较常用的设计模式。语言当然是Java,基于Spring框架。1享元模式如果系统中存在大量重复对象,或者需要不断创建和销毁大量重复对象。这时候就可以使用这种模式来缓存这些重复的对象,达到共享的目的,从而大大减少内存的占用。享元模式一般有3个角色:Flyweight:享元类(主要定义内部状态和外部状态)FlyweightFactory:享元工厂类(主要用于创建和管理享元类)1.1实际业务场景这是由实际生产事故引发,反向推送使用设计模式。因为与营销有关,所以很多用户注册页面(登陆页面)都会使用国家代码和电话国家代码。作为基础数据,我们并没有存放在这里,当时连缓存都没有做。相关Feign接口调用完全由基础服务组提供。说白了,我们是中间人。唯一要做的就是根据用户IP识别当前国家和地区,存储Top15热点国家和地区的数据。我记得当时这个数据大概有30KB。也就是说,对于每一个请求,都会在堆中创建这样一个对象,然后它就变成了垃圾对象。当时我在做问卷调查,会推送给所有APP用户。一开始只有一台服务器没有反应,然后第二台服务器在重启机器的时候被告警组通知了。经过排查,结合网上最新内容和接口请求监控数据。经判断是由于该国家和地区的界面访问量突然增加所致。但是具体的原因和解决办法需要看具体的代码逻辑才能确定。介绍完业务背景,再来说说通用的数据结构。地区元数据{"code":"US","nameZh":"UnitedStates","nameEn":"UnitedStates","tel":"+1","pyName":"mg","sortNo":1、"areaId":null}整体结构{"allCountry":[],"commonCountry":[],"currentCountry":{}}在这个数据中很容易看出。allCountry基本上不会改变,可以认为是一个内部状态。当然,这也是最占空间的地方。commonCountry在一定时间内基本不会发生变化,但是我们这里把它当作一个外部状态(内部实现可以通过缓存或者其他方式实现)。currentCountry需要根据请求IP动态生成,作为外部变量处理。至此,整体思路就清晰了。1.2国家信息代码实现POJO@DatapublicclassCountry{privateStringcode;私有字符串名称Zh;私有字符串名称En;私人字符串电话;私有字符串pyName;私人字符串排序号;privateIntegerareaId;}Flyweightclass(internalstate,externalstate)@GetterpublicclassCountryList{//内部状态privatefinalListallCountry;//外部状态privateListcommonCountry;私人国家当前国家;//内部状态,创建对象时设置publicCountryList(ListallCountry){this.allCountry=allCountry;}publicvoidsetCommonCountry(ListcommonCountry){this.commonCountry=commonCountry;}publicvoidsetCurrentCountry(CountrycurrentCountry){this.currentCountry=currentCountry;}}享元工厂类@Slf4j@Component@RequiredArgsConstructorpublicclassCountryListFactory{privatestaticfinalStringDEFAULT="DEFAULT";私人静态最终地图CL_MAP=Maps.newHashMap();私人最终CountryClientcountryClient;@PostConstructpublicvoidinit(){finalListcountryList=countryClient.getCountryList();CL_MAP.put(DEFAULT,newCountryList(countryList));publicstaticCountryListgetDefaultCl(){returnCL_MAP.get(DEFAULT);}}因为本例中享元类内部状态只有一个,所以加上DEFAULTkey值,实际上只有这一个。你在这里找到什么了吗???如果没有DEFAULT,这不是单例模式吗???这篇文章之所以这样写,主要是为了更好的理解享元模式本身。CountryClient模拟对外Feign接口服务,提供全国数据获取服务。并通过@PostConstruct将数据注入静态字典。通过这个工厂类,可以得到CountryList,根据需要将外部状态注入到这个类中,就可以使用了。1.4单机测试@SpringBootTestclassCountryListTest{@TestvoidtestFactory(){finalCountryListcl=CountryListFactory.getDefaultCl();assertNotNull(cl);}}2思维模式和单例模式有什么区别?单例主要在于单一性,即全局只有一个实例;而flyweight主要在于sharing,也就是共享。在某种程度上,单例可以被认为是享元的一种特例。享元更重要的是通过共享来达到节省内存的目的。缓存之类的东西?缓存的主要目的是加快访问速度。享元是为了数据共享和节省空间。缓存是一种思路,缓存可以通过享模式来实现。其他一些想法?享元类似于单例,可能涉及到线程安全问题?主要看看问题的角度,不冲突。说到并发,就必须考虑线程安全问题。不用享元模式也能达到同样的效果吗?设计模式是针对特定(某些场景)的通用解决方案,灵活性也很重要。先有问题再有解决方案,不是先有设计模式再有具体场景。封面图片来源:https://refactoring.guru/desi...echo'5Y6f5Yib5paH56ugOiDmjpjph5Eo5L2g5oCO5LmI5Zad5aW26Iy25ZWKWzkyMzI0NTQ5NzU1NTA4MF0pL+aAneWQpihscGUyMzQp'|base64-d