设计模式之享元模式
享元模式
享元模式是一种结构型设计模式, 它摒弃了在每个对象中保存所有数据的方式, 通过共享多个对象所共有的相同状态, 让你能在有限的内存容量中载入更多对象。其本质就是缓存共享对象,降低内存消耗。
享元模式将需要重复使用的对象分为两个部分:内部状态和外部状态。
内部状态是不会变化的,可以被多个对象共享,而外部状态会随着对象的使用而改变。比如,连接池中的连接对象,保存在连接对象中的用户名、密码、连接URL等信息,在创建对象的时候就设置好了,不会随环境的改变而改变,这些为内部状态。而当每个连接要被回收利用时,我们需要将它标记为可用状态,这些为外部状态
应用场景
仅在程序必须支持大量对象且没有足够的内存容量时使用享元模式
应用该模式所获的收益大小取决于使用它的方式和情景。 它在下列情况中最有效:
- 程序需要生成数量巨大的相似对象,这将耗尽目标设备的所有内存
- 对象中包含可抽取且能在多个对象间共享的重复状态。
例如:
- 当程序中需要创建大量的相似对象时,可以使用享元模式来共享相同的内部状态,减少对象的重复创建,节省内存开销
- 当系统运行效率较低,存在大量的内存开销时,可以采用享元模式来优化内存使用,加快程序的执行速度
- 当系统中的对象访问比较频繁时,可以使用享元模式来优化系统性能,减少重复对象的创建和销毁,提高程序的运行效率
- 当系统中需要共享一些公共对象或静态数据时,可以使用享元模式来降低系统的开销,提升系统的性能。
- 当系统需要考虑加强安全控制时,可以使用享元模式来保证对对象访问的控制,实现权限控制、缓存控制等功能。
从上述不难看出,享元模式适用于需要创建大量相似的对象以及共享公共信息的场景,同时也适用于需要考虑性能优化和共享控制的系统。但是使用之前一定要考虑清楚,还是那句话,不要为了使用设计模式而去使用设计模式。
实现方式
- 将需要改写为享元的类成员变量拆分为两个部分:
- 内在状态: 包含不变的、 可在许多对象中重复使用的数据的成员变量。
- 外在状态: 包含每个对象各自不同的情景数据的成员变量
- 保留类中表示内在状态的成员变量, 并将其属性设置为不可修改。 这些变量仅可在构造函数中获得初始数值。
- 找到所有使用外在状态成员变量的方法, 为在方法中所用的每个成员变量新建一个参数, 并使用该参数代替成员变量。
- 你可以有选择地创建工厂类来管理享元缓存池, 它负责在新建享元时检查已有的享元。 如果选择使用工厂, 客户端就只能通过工厂来请求享元, 它们需要将享元的内在状态作为参数传递给工厂。
- 客户端必须存储和计算外在状态 (情景) 的数值, 因为只有这样才能调用享元对象的方法。 为了使用方便, 外在状态和引用享元的成员变量可以移动到单独的情景类中。
代码示例
public abstract class Car {
protected String color;
protected double price;
public Car(String color, int price){
this.color=color;
this.price=price;
}
//展示汽车信息
public abstract void show();
}
public class BaoMa extends Car {
public BaoMa(String color, int price) {
super(color, price);
}
@Override
public void show() {
System.out.println("生产成功:宝马 颜色="+color+","+"起售价="+price+"元");
}
}
public class BYD extends Car {
public BYD(String color, int price) {
super(color, price);
}
@Override
public void show() {
System.out.println("生产成功:比亚迪 颜色="+color+","+" 起售价="+price+"元");
}
}
public class CarFactory {
public static Map<String, Car> carMap = new HashMap<>();
public static Car getCar(String color, String type,Double price){
String key=color+"_"+type;
if(carMap.containsKey(key)){
//如果已经有该颜色和类型的汽车,直接返回
System.out.println("已经有相同汽车,直接从carMap中获取 ");
return carMap.get(key);
}else {
Car car =null;
//没有,创建并放入缓存
if("BYD".equals(type)){
car =new BYD(color,10000);
}else if ("BaoMa".equals(type)){
car =new BaoMa(color,2000);
}else {
System.out.println("抛异常!没有该类型的汽车");
}
//放入缓存
carMap.put(key, car);
return car;
}
}
}
优缺点
优点
如果程序中有很多相似对象, 那么你将可以节省大量内存。
缺点
- 你可能需要牺牲执行速度来换取内存, 因为他人每次调用享元方法时都需要重新计算部分情景数据。
- 代码会变得更加复杂。程序员必须时刻根据系统的实际情况以及内部状态和外部状态的不同选择使用对象池或享元工厂来管理内存和共享对象,进一步增加了系统的复杂度。
本文来自博客园,作者:笨笨的二黄子,转载请注明原文链接:https://www.cnblogs.com/zwhdd/articles/17838344.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix