设计模式---享元模式

立山  金牌会员 | 2022-9-26 00:52:58 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 916|帖子 916|积分 2748

简述


  • 类型:结构型
  • 目的:降低对象创建时大量属性也随之被新建而带来的性能上的消耗
话不多说,我们看一个案例。
优化案例

最初版v0

现在需要采购一批办公用的电脑,以下是Computer类的定义。
  1. class Computer {
  2.            private String sn; // 序列号,电脑的唯一识别码
  3.     private String brand; // 品牌
  4.     private String title; // 一个系列的名称,如Lenovo的Thinkpad
  5.     private String cpu;
  6.     private String memory;
  7.     private String disk;
  8.     private String gpu;
  9.     private String keyboard;
  10.     private String display;
  11.     public Computer(String sn, String brand,
  12.                     String title, String cpu,
  13.                     String memory, String disk,
  14.                     String gpu, String keyboard,
  15.                     String display) {
  16.         this.sn = sn;
  17.         this.brand = brand;
  18.         this.title = title;
  19.         this.cpu = cpu;
  20.         this.memory = memory;
  21.         this.disk = disk;
  22.         this.gpu = gpu;
  23.         this.keyboard = keyboard;
  24.         this.display = display;
  25.     }
  26. }
复制代码
现在公司要采购两种电脑总计1000台,以下是模拟采购的代码。
  1. class Client {
  2.     public static void main(String[] args) {
  3.         List<Computer> purchase = new ArrayList<>();
  4.         for (int i = 0; i < n; i ++) {
  5.             purchase.add(new Computer(UUID.randomUUID().toString(),
  6.                          "华为", "MateBook16", "锐龙7 5800H标压",
  7.                          "16GB DDR4 双通道", "512GB NVMe PCle SSD",
  8.                          "gpu", "全尺寸背光键盘", "16英寸");
  9.         }
  10.     }
  11. }
复制代码
循环中每一次都要生成一个新的Computer对象,并且该对象中有很多String类型的属性,因为String是一个引用数据类型,所以会随之生成很多的引用,从而降低系统的性能。实际上,采购的计算机只要型号相同,配置参数也就随之相同且不会再改变,唯一会改变的其实就只有机器的序列号而已,所以我们没有每追加一台电脑就重新设置一遍所有参数的必要。而且如果中途需要对于采购订单的机器参数进行修改,那就必须迭代清单中的所有对象,对每个对象进行修改,又是一件效率低下的事。
为了解决这个问题,我们引入了享元模式。下面是修改后的代码。
修改版v1
  1. class Computer {
  2.            private String sn; // 序列号,电脑的唯一识别码
  3.     private ComputerSpec spec; // 依赖规格的具体属性 → 依赖ComputerSpec类,迪米特法则
  4.     public Computer(String sn, ComputerSpec spec) {
  5.         this.sn = sn;
  6.         this.spec = spec;
  7.         this.title = title;
  8.         this.model = model;
  9.         this.cpu = cpu;
  10.         this.memory = memory;
  11.         this.disk = disk;
  12.         this.gpu = gpu;
  13.         this.keyboard = keyboard;
  14.         this.display = display;
  15.     }
  16. }
  17. enum ComputerSpec { // 定义一个计算机规格类
  18.     MATEBOOK16("华为", "MateBook16", "锐龙7 5800H标压",
  19.                "16GB DDR4 双通道", "512GB NVMe PCle SSD",
  20.                "gpu", "全尺寸背光键盘", "16英寸");
  21.     public String brand; // 品牌
  22.     public String title; // 一个系列的名称,如Lenovo的Thinkpad
  23.     public String cpu;
  24.     public String memory;
  25.     public String disk;
  26.     public String gpu;
  27.     public String keyboard;
  28.     public String display;
  29.     ComputerSpec(String sn, String brand,
  30.                  String title, String cpu,
  31.                  String memory, String disk,
  32.                  String gpu, String keyboard,
  33.                  String display) {
  34.         this.brand = brand;
  35.         this.title = title;
  36.         this.model = model;
  37.         this.cpu = cpu;
  38.         this.memory = memory;
  39.         this.disk = disk;
  40.         this.gpu = gpu;
  41.         this.keyboard = keyboard;
  42.         this.display = display;
  43.     }
  44. }
复制代码
来看看修改后的采购如何模拟实现。
  1. class Client {
  2.     public static void main(String[] args) {
  3.         List<Computer> purchase = new ArrayList<>();
  4.         for (int i = 0; i < n; i ++) {
  5.             purchase.add(new Computer(UUID.randomUUID().toString(),
  6.                                       ComputerSpec.MATEBOOK16));
  7.         }
  8.         // 由于订单错误,现在需要批量将MateBook16修改为MateBook16s
  9.         ComputerSpec.MATEBOOK16.title = "MateBook16s";
  10.     }
  11. }
复制代码
使用享元模式,将Computer对象创建时不变的属性封装到ComputerSpec中,内部状态外部状态分开,内部状态直接引用相同的数据源,而不是每次都重新生成新的数据,从而大幅提升系统性能。并且,需要对于数据统一修改时,由于数据源引用相同,只需要修改内部状态的对应属性即可修改所有数据。

  • 内部状态:不可变对象。被共享的数据。如,案例中的ComputerSpec。
  • 外部状态:随着业务而改变数据。不被共享的数据。如,案例中的sn。
总结

优点


  • 由于多个对象的属性引用相同,从而极大程度的降低了系统性能的消耗。
  • 由于多个属性被封装成新的类,对象与属性间的依赖减少,从而降低了对象创建的复杂度。
缺点


  • 增加了开发人员对于系统业务理解的难度。
适用场景


  • 当对象的绝大多数属性与对象本身不是一对一而是一对多的关系时。换言之,多个对象公用一套属性时

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

立山

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表