话不多说,我们看一个案例。
现在需要采购一批办公用的电脑,以下是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是一个引用数据类型,所以会随之生成很多的引用,从而降低系统的性能。实际上,采购的计算机只要型号相同,配置参数也就随之相同且不会再改变,唯一会改变的其实就只有机器的序列号而已,所以我们没有每追加一台电脑就重新设置一遍所有参数的必要。而且如果中途需要对于采购订单的机器参数进行修改,那就必须迭代清单中的所有对象,对每个对象进行修改,又是一件效率低下的事。
为了解决这个问题,我们引入了享元模式。下面是修改后的代码。
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
。