设计模式---享元模式
简述
- 类型:结构型
- 目的:降低对象创建时大量属性也随之被新建而带来的性能上的消耗
话不多说,我们看一个案例。
优化案例
最初版v0
现在需要采购一批办公用的电脑,以下是 Computer
类的定义。
class Computer { private String sn; // 序列号,电脑的唯一识别码 private String brand; // 品牌 private String title; // 一个系列的名称,如Lenovo的Thinkpad private String cpu; private String memory; private String disk; private String gpu; private String keyboard; private String display; public Computer(String sn, String brand, String title, String cpu, String memory, String disk, String gpu, String keyboard, String display) { this.sn = sn; this.brand = brand; this.title = title; this.cpu = cpu; this.memory = memory; this.disk = disk; this.gpu = gpu; this.keyboard = keyboard; this.display = display; } }
现在公司要采购两种电脑总计1000台,以下是模拟采购的代码。
class Client { public static void main(String[] args) { List<Computer> purchase = new ArrayList<>(); for (int i = 0; i < n; i ++) { purchase.add(new Computer(UUID.randomUUID().toString(), "华为", "MateBook16", "锐龙7 5800H标压", "16GB DDR4 双通道", "512GB NVMe PCle SSD", "gpu", "全尺寸背光键盘", "16英寸"); } } }
循环中每一次都要生成一个新的Computer对象,并且该对象中有很多String类型的属性,因为String是一个引用数据类型,所以会随之生成很多的引用,从而降低系统的性能。实际上,采购的计算机只要型号相同,配置参数也就随之相同且不会再改变,唯一会改变的其实就只有机器的序列号而已,所以我们没有每追加一台电脑就重新设置一遍所有参数的必要。而且如果中途需要对于采购订单的机器参数进行修改,那就必须迭代清单中的所有对象,对每个对象进行修改,又是一件效率低下的事。
为了解决这个问题,我们引入了享元模式。下面是修改后的代码。
修改版v1
class Computer { private String sn; // 序列号,电脑的唯一识别码 private ComputerSpec spec; // 依赖规格的具体属性 → 依赖ComputerSpec类,迪米特法则 public Computer(String sn, ComputerSpec spec) { this.sn = sn; this.spec = spec; this.title = title; this.model = model; this.cpu = cpu; this.memory = memory; this.disk = disk; this.gpu = gpu; this.keyboard = keyboard; this.display = display; } } enum ComputerSpec { // 定义一个计算机规格类 MATEBOOK16("华为", "MateBook16", "锐龙7 5800H标压", "16GB DDR4 双通道", "512GB NVMe PCle SSD", "gpu", "全尺寸背光键盘", "16英寸"); public String brand; // 品牌 public String title; // 一个系列的名称,如Lenovo的Thinkpad public String cpu; public String memory; public String disk; public String gpu; public String keyboard; public String display; ComputerSpec(String sn, String brand, String title, String cpu, String memory, String disk, String gpu, String keyboard, String display) { this.brand = brand; this.title = title; this.model = model; this.cpu = cpu; this.memory = memory; this.disk = disk; this.gpu = gpu; this.keyboard = keyboard; this.display = display; } }
来看看修改后的采购如何模拟实现。
class Client { public static void main(String[] args) { List<Computer> purchase = new ArrayList<>(); for (int i = 0; i < n; i ++) { purchase.add(new Computer(UUID.randomUUID().toString(), ComputerSpec.MATEBOOK16)); } // 由于订单错误,现在需要批量将MateBook16修改为MateBook16s ComputerSpec.MATEBOOK16.title = "MateBook16s"; } }
使用享元模式,将 Computer
对象创建时不变的属性封装到 ComputerSpec
中, 内部状态
与 外部状态
分开,内部状态直接引用相同的数据源,而不是每次都重新生成新的数据,从而大幅提升系统性能。并且,需要对于数据统一修改时,由于数据源引用相同,只需要修改内部状态的对应属性即可修改所有数据。
ComputerSpec sn
总结
优点
- 由于多个对象的属性引用相同,从而极大程度的降低了系统性能的消耗。
- 由于多个属性被封装成新的类,对象与属性间的依赖减少,从而降低了对象创建的复杂度。
缺点
- 增加了开发人员对于系统业务理解的难度。
适用场景
- 当对象的绝大多数属性与对象本身不是一对一而是一对多的关系时。 换言之,多个对象公用一套属性时 。
「其他文章」
- 记一次批量更新整型类型的列 → 探究 UPDATE 的使用细节
- 编码中的Adapter,不仅是一种设计模式,更是一种架构理念与解决方案
- 线程池底层原理详解与源码分析
- 30分钟掌握 Webpack
- 线性回归大结局(岭(Ridge)、 Lasso回归原理、公式推导),你想要的这里都有
- Django 之路由层
- 【前端必会】webpack loader 到底是什么
- day42-反射01
- 中心化决议管理——云端分析
- HashMap底层原理及jdk1.8源码解读
- 详解JS中 call 方法的实现
- 打印 Logger 日志时,需不需要再封装一下工具类?
- 初识设计模式 - 代理模式
- 设计模式---享元模式
- 密码学奇妙之旅、01 CFB密文反馈模式、AES标准、Golang代码
- [ML从入门到入门] 支持向量机:从SVM的推导过程到SMO的收敛性讨论
- 从应用访问Pod元数据-DownwardApi的应用
- Springboot之 Mybatis 多数据源实现
- Java 泛型程序设计
- CAS核心思想、底层实现