设计模式-建造者模式
建造者模式
文章目录
- 建造者模式
- 什么是建造者模式
- 为什么要用建造者模式
- 如何实现建造者模式
- 与工厂模式有何区别
- 建造者模式有何缺陷
- 总结
什么是建造者模式
建造模式(Builder Pattern)是对象的创建模式。它可以将一个产品的内部表象与产品的生成过程分割开来,从而可以使一个建造过程生成具有不同的内部表象的产品对象。
简单理解的话就是不再使用简单的new关键字创建对象,然后使用setXxx的方式设置属性,而是将对象的创建和属性的设置相关联。
为什么要用建造者模式
当一个类的属性非常多,有的必填有的非必填,且有的属性之间有依赖关系。此时,单纯的使用new关键字和setXxx的话,不能优雅的拆创建对象,就比较适合使用建造者。举个例子:
我们需要定义连接池类 ConnectPool。属性值列表如下
属性名称 | 中文释义 | 是否必填 | 默认值 |
---|---|---|---|
name | 名称 | 是 | – |
maxConnect | 最大连接数 | 否 | 8 |
minConnect | 最小连接数 | 否 | 1 |
那么我们在创建这个ConnectPool类对象的时候,在使用建造者之前我们的构造函数是这样的:
public class ConnectPool{private static final int DEFAULT_MAX = 8;private static final int DEFAULT_MIN = 1;private String name;private int maxConnect= DEFAULT_MAX;private int minConnect= DEFAULT_MIN;public ConnectPool(String name, Integer maxConnect, Integer minConnect) {if (StringUtils.isBlank(name)) {throw new IllegalArgumentException("name should not be empty.");}this.name = name;if (maxConnect!= null) {if (maxConnect<= 1) {throw new IllegalArgumentException("maxConnect should be positive.");}this.maxConnect= maxConnect;}if (minConnect!= null) {if (minConnect< 1) {throw new IllegalArgumentException("maxConnect should not be negative.");}this.minConnect= minConnect;}}//...省略getter方法...
}
现在只有3个属性,我们的构造函数就已经非常臃肿了,那么怎么解决构造函数臃肿的问题呢,你可能想到使用set属性值的方式,那么属性值的校验就必须放在构造函数外面,显然这是一个不可把控的风险(你不能确保后面使用这个类的同事在构造的时候进行校验)。
此时其实建造者模式就可以排上用场了,我们可以把校验逻辑放置到 Builder 类中,先创建建造者,并且通过 set() 方法设置建造者的变量值,然后在使用 build() 方法真正创建对象之前,做集中的校验,校验通过之后才会创建对象。除此之外,我们把 ConnectPool的构造函数改为 private 私有权限。这样我们就只能通过建造者来创建 ConnectPool类对象。并且,ConnectPool没有提供任何 set() 方法,这样我们创建出来的对象就是不可变对象了。
如何实现建造者模式
建造者模式一般都以Builder结尾,我们直接代码示例:
public class ConnectPool{private String name;private int maxConnect;private int minConnect;private ConnectPool(Builder builder) {this.name = builder.name;this.maxConnect= builder.maxConnect;this.minConnect= builder.minConnect;}//...省略getter方法...//我们将Builder类设计成了ConnectPool的内部类。//我们也可以将Builder类设计成独立的非内部类ConnectPoolBuilder。public static class Builder {private static final int DEFAULT_MAX = 8;private static final int DEFAULT_MIN = 1;private String name;private int maxConnect= DEFAULT_MAX;private int minConnect= DEFAULT_MIN;public ConnectPoolbuild() {// 校验逻辑放到这里来做,包括必填项校验、依赖关系校验、约束条件校验等if (StringUtils.isBlank(name)) {throw new IllegalArgumentException("...");}if (minConnect> maxConnect) {throw new IllegalArgumentException("...");}return new ConnectPool(this);}public Builder setName(String name) {if (StringUtils.isBlank(name)) {throw new IllegalArgumentException("...");}this.name = name;return this;}public Builder setMaxConnect(int maxConnect) {if (maxConnect<= 1) {throw new IllegalArgumentException("...");}this.maxConnect= maxConnect;return this;}public Builder setMinConnect(int minConnect) {if (minConnect< 1) {throw new IllegalArgumentException("...");}this.minConnect= minConnect;return this;}}
}// 这段代码会抛出IllegalArgumentException,因为minIdle>maxIdle
ConnectPoolconfig = new ConnectPool.Builder().setName("dbconnectionpool").setMaxConnect(16).setMinConnect(10).build();
与工厂模式有何区别
- 工厂模式是用来创建不同但是相关类型的对象(继承同一父类或者接口的一组子类),由给定的参数来决定创建哪种类型的对象。
- 建造者模式是用来创建一种类型的复杂对象,通过设置不同的可选参数,“定制化”地创建不同的对象。
建造者模式有何缺陷
建造者模式来构建对象, 类中的成员变量,要在 Builder 类中重新再定义一遍,会使代码略显重复。
总结
何时使用建造者模式我们可以这样来判别:
- 1.需要生成的产品对象有复杂的内部结构。
- 2.需要生成的产品对象的属性相互依赖。
- 3.在对象创建过程中会使用到系统中的其他一些对象,这些对象在产品对象的创建过程中不易得到。