- 结构型模式:亨元模式
- 亨元模式
- 1、亨元模式:介绍
- 2、亨元模式:模拟场景
- 3、亨元模式:代码实现
- 4、亨元模式:总结
- 亨元模式
- 主要用于:共享通用对象,减少内存的使用,提升系统的访问效率。
- 亨元模式可以分为在服务端和在客户端。
- 服务端:数据库连接池的使用、多线程线程池的使用。
- 客户端:游戏场景中,需要地图渲染,使用亨元模式共享对象
- 在亨元模式的实现过程中,需要用到亨元工厂管理独立的对象和共享的对象,避免出现线程安全的问题。
- 缓存优化查询场景
- 模拟商品秒杀场景中使用亨元模式优化查询
- 模拟使用亨元模式搭建工厂结构,提供活动商品的查询服务。
- 商品相当于不变的信息,商品库存相当于变化的信息。
- 外观模式,结合SpringBoot 中自定义 starter 中间件开发的方式,统一处理所有需要开白名单逻辑的代码
- SpringBoot 的 starter 中间件开发方式
- 面向切面编程和自定义注解的使用方法
- 外部自定义配置信息的透传
0、工程结构
lino-design-14.0
|——src
|——main
|--java
|--com.lino.design
|--util
|--RedisUtils.java
|--Activity.java
|--ActivityController.java
|--ActivityFactory.java
|--Stock.java
|--test
|--java
|--com.lino.test
|--Test.java
- 左侧构建的是亨元工厂,提供固定活动数据的查询功能。
- 右侧是 Redis 存放的库存数据,最终交给活动控制类 ActivityController,处理查询操作,并提供活动的所有商品信息和库存信息。
- 因为库存信息是变化的,所以在模拟的 RedisUtils 中设置了定时任务消耗商品库存
1、商品活动库存信息类
- 商品库存数据,单独提供了一个类用于保存数据。
- 因为有单独类用户存储商品库存,就可以把一个简单的类存放到 Redis 中,而不需要把整个商品活动信息类都存入其中。
public class Stock {
private int total;
private int used;
public Stock(int total, int used) {
this.total = total;
this.used = used;
}
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public int getUsed() {
return used;
}
public void setUsed(int used) {
this.used = used;
}
}
2、商品活动信息类
import java.util.Date;
public class Activity {
private Long id;
private String name;
private String desc;
private Date startTime;
private Date stopTime;
private Stock stock;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public Date getStartTime() {
return startTime;
}
public void setStartTime(Date startTime) {
this.startTime = startTime;
}
public Date getStopTime() {
return stopTime;
}
public void setStopTime(Date stopTime) {
this.stopTime = stopTime;
}
public Stock getStock() {
return stock;
}
public void setStock(Stock stock) {
this.stock = stock;
}
}
3、亨元工厂
- 亨元工厂,通过 Map 结构存放已经从库表或接口中查询到的数据,并存放到内存中,方便下次直接获取。
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class ActivityFactory {
static Map activityMap = new HashMap<>();
public static Activity getActivity(Long id) {
Activity activity = activityMap.get(id);
if (null == activity) {
// 模拟实际业务应用从接口中获取活动信息
activity = new Activity();
activity.setId(10001L);
activity.setName("图书嗨乐");
activity.setDesc("图书优惠券分享激励分享活动第二期");
activity.setStartTime(new Date());
activity.setStopTime(new Date());
activityMap.put(id, activity);
}
return activity;
}
}
4、模拟Redis服务
- 模拟 Redis 的操作工具类
- 提供定时任务,用于模拟库存消耗。
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class RedisUtils {
private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
private AtomicInteger stock = new AtomicInteger(0);
public RedisUtils() {
scheduledExecutorService.scheduleAtFixedRate(() -> {
// 模拟库存消耗
stock.addAndGet(1);
}, 0, 100000, TimeUnit.MICROSECONDS);
}
public int getStockUsed() {
return stock.get();
}
}
5、活动控制类
- 在活动控制类中使用了亨元工厂获取活动信息,查询后将商品库存信息再补充到商品活动库存信息对应的库存属性中。
- 因为库存信息是变化的,而商品活动信息是固定不变的。最终,通过统一的控制类,就可以把完整包装后的商品活动信息返回给调用方。
import com.lino.util.RedisUtils;
public class ActivityController {
private RedisUtils redisUtils = new RedisUtils();
public Activity queryActivityInfo(Long id) {
Activity activity = ActivityFactory.getActivity(id);
// 模拟从Redis中获取库存变化信息
Stock stock = new Stock(1000, redisUtils.getStockUsed());
activity.setStock(stock);
return activity;
}
}
6、单元测试
import com.alibaba.fastjson.JSON;
import com.lino.Activity;
import com.lino.ActivityController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Test {
private Logger logger = LoggerFactory.getLogger(Test.class);
private ActivityController activityController = new ActivityController();
@org.junit.Test
public void testQueryActivityInfo() throws InterruptedException {
for (int idx = 0; idx < 10; idx++) {
Long req = 10001L;
Activity activity = activityController.queryActivityInfo(req);
logger.info("测试结果:{} {}", req, JSON.toJSONString(activity));
Thread.sleep(1200);
}
}
}
- 测试结果
09:14:45.919 [main] INFO com.lino.test.Test - 测试结果:10001 {"desc":"图书优惠券分享激励分享活动第二期","id":10001,"name":"图书嗨乐","startTime":1634260485827,"stock":{"total":1000,"used":1},"stopTime":1634260485827}
09:14:47.124 [main] INFO com.lino.test.Test - 测试结果:10001 {"desc":"图书优惠券分享激励分享活动第二期","id":10001,"name":"图书嗨乐","startTime":1634260485827,"stock":{"total":1000,"used":13},"stopTime":1634260485827}
09:14:48.345 [main] INFO com.lino.test.Test - 测试结果:10001 {"desc":"图书优惠券分享激励分享活动第二期","id":10001,"name":"图书嗨乐","startTime":1634260485827,"stock":{"total":1000,"used":25},"stopTime":1634260485827}
09:14:49.549 [main] INFO com.lino.test.Test - 测试结果:10001 {"desc":"图书优惠券分享激励分享活动第二期","id":10001,"name":"图书嗨乐","startTime":1634260485827,"stock":{"total":1000,"used":38},"stopTime":1634260485827}
09:14:50.753 [main] INFO com.lino.test.Test - 测试结果:10001 {"desc":"图书优惠券分享激励分享活动第二期","id":10001,"name":"图书嗨乐","startTime":1634260485827,"stock":{"total":1000,"used":50},"stopTime":1634260485827}
09:14:51.966 [main] INFO com.lino.test.Test - 测试结果:10001 {"desc":"图书优惠券分享激励分享活动第二期","id":10001,"name":"图书嗨乐","startTime":1634260485827,"stock":{"total":1000,"used":62},"stopTime":1634260485827}
09:14:53.171 [main] INFO com.lino.test.Test - 测试结果:10001 {"desc":"图书优惠券分享激励分享活动第二期","id":10001,"name":"图书嗨乐","startTime":1634260485827,"stock":{"total":1000,"used":74},"stopTime":1634260485827}
09:14:54.378 [main] INFO com.lino.test.Test - 测试结果:10001 {"desc":"图书优惠券分享激励分享活动第二期","id":10001,"name":"图书嗨乐","startTime":1634260485827,"stock":{"total":1000,"used":86},"stopTime":1634260485827}
09:14:55.582 [main] INFO com.lino.test.Test - 测试结果:10001 {"desc":"图书优惠券分享激励分享活动第二期","id":10001,"name":"图书嗨乐","startTime":1634260485827,"stock":{"total":1000,"used":98},"stopTime":1634260485827}
09:14:56.790 [main] INFO com.lino.test.Test - 测试结果:10001 {"desc":"图书优惠券分享激励分享活动第二期","id":10001,"name":"图书嗨乐","startTime":1634260485827,"stock":{"total":1000,"used":110},"stopTime":1634260485827}
4、亨元模式:总结
- 可以着重学习亨元工厂的实现方式,有一些有大量重复对象可复用的场景中
- 主要特色:在服务端减少接口的调用,在客户端减少内存的占用
- 另外:通过 Map 结构,使用一个固定的 ID 存放和获取对象是非常关键的。
- 缺点:在一些复杂业务处理场景中,不容易区分内部状态和外部状态。



