享元模式
概述
享元模式(英语:Flyweight Pattern)是一种软件设计模式。它使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件;它适合用于只是因重复而导致使用无法令人接受的大量内存的大量物件。通常物件中的部分状态是可以分享。常见做法是把它们放在外部数据结构,当需要使用时再将它们传递给享元。
关于享元模式,一言以蔽之就是“通过尽量共享实例来避免new出新的实例”。
示例程序
大家肯定都有使用过淘宝、京东等一些购物软件,在商品详情中,几乎所有的数据都是不会发生变化的。用户在短期内访问该商品,变化的一般只有库存,如果我们new出新的实例,那每一个用户来访问,内存中都会多出一个新的实例,这时候我们应该将商品的大部分不变的信息作为共享实例。
UML类图
名字 | 说明 |
---|---|
Commodity | 不变的商品信息 |
Stock | 变化的商品库存信息 |
CommodityFactory | 商品工厂,用于生成Commodity |
RedisUtils | 模拟redis中读取商品库存 |
CommodityController | 读取商品信息的接口 |
代码实现
Commodity
@Data
public class Commodity {
private Integer id;//商品id
private String name;//商品名称
private BigDecimal price;//商品价格
private Stock stock;//库存信息
}
Stock
@Data
@AllArgsConstructor
public class Stock {
private Integer total;//商品总数
private Integer used;//已用数量
}
CommodityFactory
public class CommodityFactory {
//核心思想在于将不需要变动的东西存入map中
static Map<Integer, Commodity> commodityMap = new HashMap<>();
public static Commodity getCommodity(Integer id){
Commodity commodity = commodityMap.get(id);
if (commodity == null){
//实际业务中,图书信息因从数据库读取
commodity = new Commodity();
commodity.setId(id);
commodity.setName("图解设计模式");
commodity.setPrice(new BigDecimal(100));
commodityMap.put(id,commodity);
}
return commodity;
}
}
RedisUtils
RedisUtils模拟库存消耗
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();
}
}
CommodityController
@RestController
public class CommodityController {
private RedisUtils redisUtils = new RedisUtils();
public Commodity queryCommodityInfo(Integer id) {
Commodity commodity = CommodityFactory.getCommodity(id);
// 模拟从Redis中获取库存变化信息
Stock stock = new Stock(1000, redisUtils.getStockUsed());
commodity.setStock(stock);
return commodity;
}
}
test
@SpringBootTest
@Slf4j
class Practice1400ApplicationTests {
@Autowired
CommodityController commodityController;
@Test
void contextLoads() throws InterruptedException {
for (int idx = 0; idx < 10; idx++) {
int id = 10001;
Commodity commodity = commodityController.queryCommodityInfo(id);
log.info("测试结果:{} ", commodity);
Thread.sleep(1200);
}
}
}
登场角色
-
Flyweight
按照通常方式编写程序会导致程序变种,所以如果能够适应共享实例会比较好,而Flyweight角色就表示哪些实例会被共享的类。在示例程序中由Commodity扮演。
-
FlyweightFactory
FlyweightFactory角色是生成Flyweight角色的工厂,在工厂中生成Flyweight角色可以实现共享实例。在示例程序中由CommodityFactory扮演该角色。
总结
享元模式的主题就是共享,那么在共享时应注意什么呢?
如果要改变被共享的对象,就会对多个地方产生影响
也就是说一个实例的改变会反映到所有使用该实例的地方,假设我们修改了商品名称,那么所有其他使用到商品名称的地方都会随之发生改变。
因此,在决定Flyweight角色时,需要精挑细选,只将哪些真正应该在多个地方共享的字段定义在Flyweight角色中。