建造者模式同样是创建型设计模式的一种,用于解决复杂对象的创建问题。
为什么需要建造者模式
为了得到一个对象,通常情况下我们调用构造方法然后再配合 set 方法做一些初始化工作即可。假定我们现在有一个传感器,传感器的名称和类型不能为空、客户ID可选、如果客户ID不为空时安装时间也不能为空,反应到类的代码如下。
建造者模式同样是创建型设计模式的一种,用于解决复杂对象的创建问题。 为什么需要建造者模式 为了得到一个对象,通常情况下我们调用构造方法然后再配合 set 方法做一些初始化工作即可。假定我们现在有一个传感器,传感器的名称和类型不能为空、客户ID可选、如果客户ID不为空时安装时间也不能为空,反应到类的代码如下。 ———————————————— 版权声明:本文为CSDN博主「大鹏cool」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/zzuhkp/article/details/11645641
为了满足传感器各属性的限制,我们定义了三个构造方法,为了复用代码,我们的实现中参数少的构造方法会调用参数多的构造方法。当前只有三个构造方法还可以接受,如果以后需要再次扩展,如温度传感器需要配置报警的最低温度和最高温度,我们还需要为构造方法增加新的参数,具体如下。
public class Sensor { private String name; private String type; private Integer customerId; private Date installTime; private Double lower; private Double upper; public Sensor(String name, String type, Integer customerId, Date installTime, Double lower, Double upper) { ... 省略部分代码 if ((lower != null && upper == null) || (upper != null && lower == null)) { throw new IllegalArgumentException("报警温度有误"); } if (lower != null && upper != null && lower > upper) { throw new IllegalArgumentException("报警温度有误"); } this.name = name; this.type = type; this.customerId = customerId; this.installTime = installTime; this.lower = lower; this.upper = upper; } }
发现问题了吗?类的构造方法参数不断的增加,最终导致可读性极差,很容易传入错误的参数,使用者必须阅读相关注释或代码才能确实使用哪个构造方法。
那怎么解决这个问题呢?按照通常的思路,调用构造方法后再调 set 方法,我们只把传感器必须的属性作为构造方法参数,其他的属性通过 set 方法设置即可。代码如下。
Sensor sensor = new Sensor("温度传感器", "温度"); sensor.setInstallTime(new Date()); sensor.setCustomerId(1);
但是问题又来了,我们是需要进行参数校验的,对于上述的场景设置了客户ID后则必须设置安装时间,我们无法保证设置安装时间方法、设置客户ID方法的调用顺序,也就是说对于有依赖关系的属性现在我们无法进行参数校验。怎么办呢?为了保证构造方法参数不断扩展又需要进行参数校验,建造者模式就出现了。
如何实现一个建造者模式
使用建造者模式解决上述问题,代码如下。
public class Sensor { private String name; private String type; private Integer customerId; private Date installTime; private Double lower; private Double upper; private Sensor(Build build) { this.name = build.name; this.type = build.type; this.customerId = build.customerId; this.installTime = build.installTime; this.lower = build.lower; this.upper = build.upper; } public static class Build { private String name; private String type; private Integer customerId; private Date installTime; private Double lower; private Double upper; public String getName() { return name; } public Build setName(String name) { this.name = name; return this; } public String getType() { return type; } public Build setType(String type) { this.type = type; return this; } public Integer getCustomerId() { return customerId; } public Build setCustomerId(Integer customerId) { this.customerId = customerId; return this; } public Date getInstallTime() { return installTime; } public Build setInstallTime(Date installTime) { this.installTime = installTime; return this; } public Double getLower() { return lower; } public Build setLower(Double lower) { this.lower = lower; return this; } public Double getUpper() { return upper; } public Build setUpper(Double upper) { this.upper = upper; return this; } public Sensor build() { if (this.name == null || this.name.isEmpty()) { throw new IllegalArgumentException("名称不能为空"); } if (this.type == null || this.type.isEmpty()) { throw new IllegalArgumentException("类型不能为空"); } if (this.customerId != null) { if (this.installTime != null && this.installTime.compareTo(new Date()) > 0) { throw new IllegalArgumentException("安装时间有误"); } } if ((this.lower != null && this.upper == null) || (this.upper != null && this.lower == null)) { throw new IllegalArgumentException("报警温度有误"); } if (this.lower != null && this.upper != null && this.lower > this.upper) { throw new IllegalArgumentException("报警温度有误"); } Sensor sensor = new Sensor(this); return sensor; } } }
使用方此时创建传感器类实例的代码如下。
Sensor sensor = new Sensor.Build().setName("温度传感器").setType("温度").setCustomerId(1).setInstallTime(new Date()).build();
我们将设置对象属性的代码转移到了建造者类中,然后在建造者类的 build 方法中完成了参数的校验。
总结
建造者模式用于解决复杂对象创建的问题,针对构造方法参数过长和具有依赖关系的参数校验问题,将参数设置的代码转移到建造者类,并通过建造者类的 build 方法完成存在依赖关系的参数校验。实现一个建造者模式通常的步骤如下。
在实体类中创建一个静态内部类 XxxBuilder,属性和实体类保持一致。
实体类提供一个私有的仅有一个类型为静态内部类的参数。
静态内部类的 setter 方法返回静态内部类实例,便于链式调用。
静态内部类提供 build 方法,用于参数校验及实例化实体类。