Builder模式有什么用?
简洁明了的使用方式、灵活多变的链式调用、复杂对象的构建、增加代码可读性
先看一个例子
package com.example.demo.builder;
public class User {
private String name;
private String sex;
private int age;
private double height;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
}
如果现在我要创建出一个User对象,那么一般我们会调用User对象的构造方法进行赋值,比如这样:
public User(String name, String sex, int age, double height) {
this.name = name;
this.sex = sex;
this.age = age;
this.height = height;
}
在使用时可以调用对应的构造方法来创建出一个User对象
new User("张三", "男", 20, 1.7)
那么问题来了,这个对象中有四个属性,分别是姓名、性别、年龄、身高,假设年龄、身高不是必须输入的字段,如何处理?该怎么去构建这个对象呢?
这时我们可以多写几个构造方法,涵盖所有属性就可以解决,比如:
package com.example.demo.builder;
public class User {
private String name;
private String sex;
private int age;
private double height;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public User(String name, String sex) {
this.name = name;
this.sex = sex;
}
public User(String name, String sex, int age) {
this.name = name;
this.sex = sex;
this.height = height;
}
public User(String name, String sex, int age, double height) {
this.name = name;
this.sex = sex;
this.age = age;
this.height = height;
}
}
多写几个构造方法不就解决了么,非常简单,对不对。
但是这样做的坏处其实已经体现的很明显了
1、参数目前我们有4个字段,如果是二十个呢?三十个呢?就会导致写非常多的构造方法,代码会变的难以阅读,而且很难维护。
2、当我调用构造方法的时候,我怎么知道我的第三第四个参数是代表什么意思?万一传参搞混了呢?参数如果非常多的话,我岂不是要写一个参数,在去看一眼构造方法,哦~原来下一个参数要传的是age而不是height,我这里一个是int一个是double,如果写反的话编译就过不去,但是如果两个类型都是一样的话,的确是有搞混的风险。
除了用构造方法外,还有有一种最常用的属性赋值的方式set方法
final User user = new User();
user.setName("张三");
user.setAge(20);
user.setSex("男");
user.setHeight(1.7);
但是这种方法也有缺点
1、对象处于不一致状态,在多线程环境下,其中一个线程创建对象,另外一个线程使用第一个线程创建的对象,就会有潜在的风险,当第一个线程执行到setSex的时候挂起,第二个线程以为对象已经创建完毕,就直接拿来使用,但是在这时候其实对象还没有创建完,就会有状态不一致的问题。
2、不够灵活,对于复杂点的对象创建起来逻辑和层次不够明显
比如,在User类中在增加一个配偶的对象
package com.example.demo.builder;
public class User {
private String name;
private String sex;
private int age;
private double height;
// 配偶
private Spouse spouse;
private static class Spouse {
private String spouseName;
private int spouseAge;
private double spouseHeight;
public String getSpouseName() {
return spouseName;
}
public void setSpouseName(String spouseName) {
this.spouseName = spouseName;
}
public int getSpouseAge() {
return spouseAge;
}
public void setSpouseAge(int spouseAge) {
this.spouseAge = spouseAge;
}
public double getSpouseHeight() {
return spouseHeight;
}
public void setSpouseHeight(double spouseHeight) {
this.spouseHeight = spouseHeight;
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public Spouse getSpouse() {
return spouse;
}
public void setSpouse(Spouse spouse) {
this.spouse = spouse;
}
public User(String name, String sex) {
this.name = name;
this.sex = sex;
}
public User() {}
public User(String name, String sex, int age) {
this.name = name;
this.sex = sex;
this.height = height;
}
public User(String name, String sex, int age, double height) {
this.name = name;
this.sex = sex;
this.age = age;
this.height = height;
}
public static void main(String[] args) {
final User user = new User();
user.setName("张三");
user.setAge(20);
user.setSex("男");
user.setHeight(1.7);
}
}
这时候在用set方法的时候,会变成这样
final User user = new User();
user.setName("张三");
user.setAge(20);
user.setSex("男");
user.setHeight(1.7);
final Spouse spouse = new Spouse();
spouse.setSpouseName("李四");
spouse.setSpouseAge(18);
spouse.setSpouseHeight(1.67);
user.setSpouse(spouse);
看起来层次感和逻辑体现的并不是特别清晰
这时就可以利用Builder模式来解决上面遇到的所有问题
Builder建造者模式
先附上代码
package com.example.demo.builder;
import lombok.Getter;
import lombok.Setter;
@Getter
public class User {
private String name;
private String sex;
private int age;
private double height;
private boolean isHaveSpouse;
private Spouse spouse;
@Getter
@Setter
private static class Spouse {
private String spouseName;
private int spouseAge;
private double spouseHeight;
private final Builder builder;
public Spouse(Builder builder) {
this.builder = builder;
}
private Spouse(String spouseName, int spouseAge, double spouseHeight, Builder builder) {
this.spouseName = spouseName;
this.spouseAge = spouseAge;
this.spouseHeight = spouseHeight;
this.builder = builder;
}
public Spouse spouseName(String spouseName) {
this.spouseName = spouseName;
return this;
}
public Spouse spouseAge(int spouseAge) {
this.spouseAge = spouseAge;
return this;
}
public Spouse spouseHeight(double spouseHeight) {
this.spouseHeight = spouseHeight;
return this;
}
public Builder end() {
this.builder.spouse = new Spouse(spouseName, spouseAge, spouseHeight, builder);
return this.builder;
}
}
public User(Builder builder) {
this.name = builder.name;
this.sex = builder.sex;
this.age = builder.age;
this.height = builder.height;
this.isHaveSpouse = builder.isHaveSpouse;
this.spouse = builder.spouse;
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private String name;
private String sex;
private int age;
private double height;
private boolean isHaveSpouse;
private Spouse spouse;
public Builder name(String name) {
this.name = name;
return this;
}
public Builder sex(String sex) {
this.sex = sex;
return this;
}
public Builder age(int age) {
this.age = age;
return this;
}
public Builder height(double height) {
this.height = height;
return this;
}
public Builder notHaveSpouse() {
this.isHaveSpouse = false;
return this;
}
public Spouse haveSpouse() {
this.isHaveSpouse = true;
return new Spouse(this);
}
public User build() {
return new User(this);
}
}
}
这里为了让代码稍微少点,用了lombok的注解,整体来看用Builder的话代码量会稍微多一些,但是在使用时会更加方便、易扩展。
编写Builder模式的几个步骤:
1、在类中创建一个静态内部类Builder,Builder类中涵盖了User类的所有属性
2、在Builder类中对每个属性赋值,与set的区别在于赋值后返回自己
3、创建build方法,用来返回外部对象User
4、在User对象中,编写一个builder的静态方法,主要用于通过类名.builder的方式获取构造对象
使用时通过类名.builder().属性()的方式来使用
User类无配偶的例子:
User build = User.builder()
.name("张三")
.sex("男")
.age(20)
.height(1.7)
.notHaveSpouse()
.build();
User类有配偶的例子:
User build = User.builder()
.name("张三")
.sex("男")
.age(20)
.height(1.7)
.haveSpouse()
.spouseName("李四")
.spouseAge(18)
.spouseHeight(1.65)
.end()
.build();
如果是不设置成有配偶,是调用不了spouseName、spouseAage…这些字段的,必须调用haveSpouse才可以调用配偶相关的方法,相当于是在创建对象时增加了条件约束,在特定方法之后才可以调用相关方法。
总结
优点:利用builder方式来构建对象的好处,从上面的两行代码就会发现,链式调用,逻辑清晰、可读性好,使用时非常简单、整洁
缺点:在编写Builder构造的过程中,代码量会比较多,也会有些逻辑在里面,在写的时候会稍微有一点复杂,不像setXXX这种方式非常的无脑
不过对于客户端使用的时候来说,非常方便,同时因为是在最后调用的build,所以也不存在不一致的问题。
留下人生足迹,一步一个脚印