书接上回
单例模式保证唯一实例,工厂模式把控创建过程
。本篇中要说到建造者模式的另一个重要成员建造者模式(Build Pattern)
。本文主要分为以下部分:
- 前言
- 建造者模式
2.1 为什么要有建造者模式
2.2 定义
2.3 实现
2.3.1 建造者模式的变种
2.3.2 标准建造者模式
2.3.3 区别及联系- 总结
1. 前言
设计模式按照模式的目标可以做如下划分:
创建型:涉及到对象的实例化,提供一个方法,将客户从对象的创建中解耦
行为型:涉及到类和对象如何交互及分配职责
结构型:结构型模式可以让你把类和对象组合到更大的对象中去
前面我们介绍了创建型模式的两位大佬,单例模式 以及 工厂模式。这篇文章里我们要重点介绍下创建者模式。
2. 建造者模式
2.1 为什么要有建造者模式
假设我们有如下的Bean,需要通过构造函数构造
public class Order {
private String orderId;
private String orderAddr;
private String orderNotes;
private String orderComments;
private String userId;
private String goodsId;
public Order(){
}
public Order(String orderId, String orderAddr, String orderNotes, String orderComments, String userId, String goodsId) {
this.orderId = orderId;
this.orderAddr = orderAddr;
this.orderNotes = orderNotes;
this.orderComments = orderComments;
this.userId = userId;
this.goodsId = goodsId;
}
}
现在我们尝试通过构造函数初始化它,我们会写出如下代码
Order order=new Order("pcm001","上海市黄浦区陆家嘴","请尽快送达","五星好评","user001","goods001");
当然这段代码运行起来是不会有任何问题的,但是如果我们不小心写反了参数的顺序会发生什么呢?
尝试将orderNotes和orderAddr赋值搞反
Order order=new Order("pcm001","请尽快送达","上海市黄浦区陆家嘴","五星好评","user001","goods001");
因为都是String类型的参数,所以编译时不会报错,程序能正常运行
,那么我们什么时候才能发现问题呢?
或许只有产生脏数据,由数据倒查的时候才会发现问题所在
。
对程序而言,我们往往希望提前暴露问题,而不是运行时才暴露问题
。
这个问题有什么好的解决方案吗?答案就在建造者模式中。
2.2 定义
构造者模式将对象的创建和对象的表示分离,使得同样的构建过程可以构建不同的对象
。正如标准设计模式的定义一样,创建型模式的定义同样晦涩难懂,只看定义,真是为难我等吃瓜群众。
别方,我们结合实例来看。
2.3 实现
2.3.1 建造者模式的变种
对于上面的Order类,我们可以做如下改造
- 定义静态内部类Builder
- 实现Builder类中的与属性名同名的赋值方法
- 实现Build类中的build方法(创建Order实例,并通过构造函数一一赋值)
- 在Order类中暴露静态builder方法,返回Builder()实例
public class Order {
private String orderId;
private String orderAddr;
private String orderNotes;
private String orderComments;
private String userId;
private String goodsId;
//私有构造函数
private Order() {
}
//私有构造函数
private Order(String orderId, String orderAddr, String orderNotes, String orderComments, String userId, String goodsId) {
this.orderId = orderId;
this.orderAddr = orderAddr;
this.orderNotes = orderNotes;
this.orderComments = orderComments;
this.userId = userId;
this.goodsId = goodsId;
}
//静态builder创建
public static Builder builder() {
return new Builder();
}
public static class Builder {
private String orderId;
private String orderAddr;
private String orderNotes;
private String orderComments;
private String userId;
private String goodsId;
//属性名相同的静态方法
public Builder orderId(String orderId) {
this.orderId = orderId;
return this;
}
public Builder orderAddr(String orderAddr) {
this.orderAddr = orderAddr;
return this;
}
public Builder orderNotes(String orderNotes) {
this.orderNotes = orderNotes;
return this;
}
public Builder orderComments(String orderComments) {
this.orderComments = orderComments;
return this;
}
public Builder userId(String userId) {
this.userId = userId;
return this;
}
public Builder goodsId(String goodsId) {
this.goodsId = goodsId;
return this;
}
public Order build() {
return new Order(this.orderId, this.orderAddr, this.orderNotes, this.orderComments, this.userId, this.goodsId);
}
}
}
使用的时候
Order order=Order.builder()
.orderId("pcm001")
.orderAddr("上海市黄浦区陆家嘴")
.orderNotes("请尽快送达")
.orderComments("五星好评")
.userId("user001")
.goodsId("goods001")
.build();
原来的赋值方式
Order order=new Order("pcm001","上海市黄浦区陆家嘴","请尽快送达","五星好评","user001","goods001");
对比下来,使用构造者模式有如下好处:
无需关心属性的赋值顺序
可以按照具体的需要单个设置属性值
赋值方法和属性名相同,不易出错
代码更简洁,富有美感
有了构造者模式,妈妈再也不用担心我的赋值
2.3.2 标准建造者模式
前面通过Order的例子介绍了构造模式的变种,下面介绍标准构造者模式。标准构造者模式包含以下组件:
- 产品类
- 抽象Builder
- 具体Builder
- Director 统一构建过程
- client
产品类-Phone
public class Phone {
private String os;
private String name;
private String display;
public String getOs() {
return os;
}
public void setOs(String os) {
this.os = os;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDisplay() {
return display;
}
public void setDisplay(String display) {
this.display = display;
}
@Override
public String toString() {
return "Phone{" +
"os='" + os + '\'' +
", name='" + name + '\'' +
", display='" + display + '\'' +
'}';
}
}
抽象Builder
public abstract class AbstractPhoneBuilder {
abstract void buildName();
abstract void buildOs();
abstract void buildDisplay();
abstract Phone build();
}
具体Builder
public class IosPhoneBuilder extends AbstractPhoneBuilder{
private Phone phone=new Phone();
@Override
void buildName() {
phone.setName("iphone");
}
@Override
void buildOs() {
phone.setOs("ios");
}
@Override
void buildDisplay() {
phone.setDisplay("Retain XDR");
}
@Override
Phone build() {
return phone;
}
}
public class AndroidPhoneBuilder extends AbstractPhoneBuilder{
private Phone phone=new Phone();
@Override
void buildName() {
phone.setName("Android");
}
@Override
void buildOs() {
phone.setOs("Android");
}
@Override
void buildDisplay() {
phone.setDisplay("svmsung XDR");
}
@Override
Phone build() {
return phone;
}
}
Director 类指导创建过程
public class Director {
public void construct(AbstractPhoneBuilder phoneBuilder){
phoneBuilder.buildDisplay();
phoneBuilder.buildName();
phoneBuilder.buildOs();
}
}
client 调用
public class Client {
public static void main(String[] args) {
AbstractPhoneBuilder iosPhoneBuilder=new IosPhoneBuilder();
Director director=new Director();
director.construct(iosPhoneBuilder);
Phone iosPhone= iosPhoneBuilder.build();
System.out.println(iosPhone);
AbstractPhoneBuilder androidPhoneBuilder=new AndroidPhoneBuilder();
director.construct(androidPhoneBuilder);
Phone androidPhone= androidPhoneBuilder.build();
System.out.println(androidPhone);
}
}
Phone{os='ios', name='iphone', display='Retain XDR'}
Phone{os='Android', name='Android', display='svmsung XDR'}
可以看到在标准建造者模式里:抽象Builder负责抽象Builder的构建行为,具体Builder实现负责实现具体部件的构建行为,而Director类负责指导构建过程(具体按照什么顺序构建,构建哪些模块)
2.3.3 区别及联系
通过上面的对比,我们可以明显看到,变种模式相对于标准模式而言:
放弃了Director 类
将Director指导创建过程的工作交给了client
同时将Builder设计产品类的内部类
-
采用了链式调用
通过上面的介绍,我们可以明显看到构造者模式的使用场景分离对象的创建过程
。
个人感觉在实际工作中,用变种构造者模式的场景要远大于标准构造者模式(标准构造者模式一般都在有默认值的时候使用)
3. 总结
本文简单介绍了建造者模式,并通过实例比对了变种建造者模式和标准建造者模式的区别。
可能有的同学会觉得建造者模式的代码繁琐,这里推荐一个小巧的插件
Lombok
。优点是省略冗余代码,简洁高效
,缺点是协调开发情况下,必须安装lombok插件,不然会代码报错。且由于是通过编译时动态修改字节码文件来实现增强,所以在.java文件中看不到源码,不利于代码的调试。
由于技术水平所限,文章难免有不足之处,欢迎大家指出。希望每位读者都能有新的收获,我们下一篇文章见.....