重学 Java 设计模式:实战享元模式「基于Redis秒杀,提供活动与库存信息查询场景」
一、前言
程序员👨💻的上下文是什么?
很多时候一大部分编程开发的人员都只是关注于功能的实现,只要自己把这部分需求写完就可以了,有点像被动的交作业。这样的问题一方面是由于很多新人还不了解程序员的职业发展,还有一部分是对于编程开发只是工作并非兴趣。但在程序员的发展来看,如果不能很好的处理上文(产品
),下文(测试
),在这样不能很好的了解业务和产品发展,也不能编写出很有体系结构的代码,日久天长,1到3年、3到5年,就很难跨越一个个技术成长的分水岭。
拥有接受和学习新知识的能力
你是否有感受过小时候在什么都还不会的时候接受知识的能力很强,但随着我们开始长大后,慢慢学习能力、处事方式、性格品行,往往会固定。一方面是形成了各自的性格特征,一方面是圈子已经固定。但也正因为这样的故步,而很少愿意听取别人的意见,就像即使看到了一整片内容,在视觉盲区下也会过掉到80%,就在眼前也看不见,也因此导致了能力不再有较大的提升。
编程能力怎样会成长的最快
工作内容往往有些像在工厂🏭拧螺丝,大部分内容是重复的,也可以想象过去的一年你有过多少创新和学习了新的技能。那么这时候一般为了多学些内容会买一些技术书籍,但!技术类书籍和其他书籍不同,只要不去用看了也就只是轻描淡写,很难接纳和理解。就像设计模式,虽然可能看了几遍,但是在实际编码中仍然很少会用,大部分原因还是没有认认真真的跟着实操。事必躬亲才是学习编程的最好是方式。
二、开发环境
- JDK 1.8
- Idea + Maven
- 涉及工程三个,可以通过关注公众号:
bugstack虫洞栈
,回复源码下载
获取(打开获取的链接,找到序号18)
工程 | 描述 |
---|---|
itstack-demo-design-11-01 | 使用一坨代码实现业务需求 |
itstack-demo-design-11-02 | 通过设计模式优化代码结构,减少内存使用和查询耗时 |
三、享元模式介绍
享元模式,主要在于共享通用对象,减少内存的使用,提升系统的访问效率。而这部分共享对象通常比较耗费内存或者需要查询大量接口或者使用数据库资源,因此统一抽离作为共享对象使用。
另外享元模式可以分为在服务端和客户端,一般互联网H5和Web场景下大部分数据都需要服务端进行处理,比如数据库连接池的使用、多线程线程池的使用,除了这些功能外,还有些需要服务端进行包装后的处理下发给客户端,因为服务端需要做享元处理。但在一些游戏场景下,很多都是客户端需要进行渲染地图效果,比如;树木、花草、鱼虫,通过设置不同元素描述使用享元公用对象,减少内存的占用,让客户端的游戏更加流畅。
在享元模型的实现中需要使用到享元工厂来进行管理这部分独立的对象和共享的对象,避免出现线程安全的问题。
四、案例场景模拟
在这个案例中我们模拟在商品秒杀场景下使用享元模式查询优化
你是否经历过一个商品下单的项目从最初的日均十几单到一个月后每个时段秒杀量破十万的项目。一般在最初如果没有经验的情况下可能会使用数据库行级锁的方式下保证商品库存的扣减操作,但是随着业务的快速发展秒杀的用户越来越多,这个时候数据库已经扛不住了,一般都会使用redis的分布式锁来控制商品库存。
同时在查询的时候也不需要每一次对不同的活动查询都从库中获取,因为这里除了库存以外其他的活动商品信息都是固定不变的,以此这里一般大家会缓存到内存中。
这里我们模拟使用享元模式工厂结构,提供活动商品的查询。活动商品相当于不变的信息,而库存部分属于变化的信息。
五、用一坨坨代码实现
逻辑很简单,就怕你写乱。一片片的固定内容和变化内容的查询组合,CV的哪里都是!
其实这部分逻辑的查询在一般情况很多程序员都是先查询固定信息,在使用过滤的或者添加if判断的方式补充变化的信息,也就是库存。这样写最开始并不会看出来有什么问题,但随着方法逻辑的增加,后面就越来越多重复的代码。
1. 工程结构
itstack-demo-design-11-01
└── src
└── main
└── java
└── org.itstack.demo.design
└── ActivityController.java
- 以上工程结构比较简单,之后一个控制类用于查询活动信息。
2. 代码实现
/**
* 博客:https://bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获!
* 公众号:bugstack虫洞栈
* Create by 小傅哥(fustack) @2020
*/
public class ActivityController {
public Activity queryActivityInfo(Long id) {
// 模拟从实际业务应用从接口中获取活动信息
Activity activity = new Activity();
activity.setId(10001L);
activity.setName("图书嗨乐");
activity.setDesc("图书优惠券分享激励分享活动第二期");
activity.setStartTime(new Date());
activity.setStopTime(new Date());
activity.setStock(new Stock(1000,1));
return activity;
}
}
- 这里模拟的是从接口中查询活动信息,基本也就是从数据库中获取所有的商品信息和库存。有点像最开始写的商品销售系统,数据库就可以抗住购物量。
- 当后续因为业务的发展需要扩展代码将库存部分交给redis处理,那么就需要从redis中获取活动的库存,而不是从库中,否则将造成数据不统一的问题。
六、享元模式重构代码
接下来使用享元模式来进行代码优化,也算是一次很小的重构。
享元模式一般情况下使用此结构在平时的开发中并不太多,除了一些线程池、数据库连接池外,再就是游戏场景下的场景渲染。另外这个设计的模式思想是减少内存的使用提升效率,与我们之前使用的原型模式通过克隆对象的方式生成复杂对象,减少rpc的调用,都是此类思想。
1. 工程结构
itstack-demo-design-11-02
└── src
├── main
│ └── java
│ └── org.itstack.demo.design
│ ├── util
│ │ └── RedisUtils.java
│ ├── Activity.java
│ ├── ActivityController.java
│ ├── ActivityFactory.java
│ └── Stock.java
└── test
└── java
└── org.itstack.demo.test
└── ApiTest.java
享元模式模型结构
- 以上是我们模拟查询活动场景的类图结构,左侧构建的是享元工厂,提供固定活动数据的查询,右侧是Redis存放的库存数据。
- 最终交给活动控制类来处理查询操作,并提供活动的所有信息和库存。因为库存是变化的,所以我们模拟的
RedisUtils
中设置了定时任务