背景
小七最近在看项目代码时,发现了一个很奇怪的类。代码大概如下:
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name) {
this.name = name;
}
public Student(int age) {
this.age = age;
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
构造函数之多,令人瞠目结舌。其实这么多的构造函数,就是想通过不同的属性创建不同的对象,联想到以前看过的《effective java》,发现作者对于可变参数对象的创建,极力推荐使用建造者模式。所以让咱们尝试改改它吧。
思考过程
首先创建一个正常的学生类
public class Student {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
然后我们把他的创建行为抽象到一个抽象类中
public abstract class StudentBuilder {
public abstract void buildName(String name);
public abstract void buildAge(int age);
public abstract Student makeStudent();
}
有了抽象类,接着需要他们实现类
public class StudentActualBuilder extends StudentBuilder {
/**
* 这里使用组合,将 student 组合到实现类中
*/
private Student student = new Student();
@Override
public void buildName(String name) {
student.setName(name);
}
@Override
public void buildAge(int age) {
student.setAge(age);
}
@Override
public Student makeStudent() {
return student;
}
}
万事俱备只差一个像工厂一样的创建者了,于是我们有了god类
public class God {
/**
* 注入StudentBuilder
*/
private StudentBuilder studentBuilder;
public void setStudentBuilder(StudentBuilder studentBuilder) {
this.studentBuilder = studentBuilder;
}
public Student makeStudent(String name,int age) {
this.studentBuilder.buildAge(age);
this.studentBuilder.buildName(name);
return this.studentBuilder.makeStudent();
}
}
最后测试一下
测试类:
public class Test {
public static void main(String[] args) {
StudentActualBuilder studentActualBuilder = new StudentActualBuilder();
God god = new God();
god.setStudentBuilder(studentActualBuilder);
Student student = god.makeStudent("第七人格", 28);
System.out.println(student);
}
}
结果:
用过StringBuilder的我们知道,StringBuilder有个append方法,所以我们学着StringBuilder将上面的代码,改为链式调用。
首先God 类只是使用了注入了StudentBuilder 对象,然后给给其赋值,其实是可以不用单独建类实现的。而StudentBuilder 及其实现类StudentActualBuilder 可以写成一个类,并且以内部内放在Student中。所以有了以下代码:
public class Student {
/**
* 姓名
*/
private String name;
/**
* 年龄
*/
private int age;
/**
* 学生类的构造函数
*
* @param name 的名字
* @param age 年龄
*/
Student(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 构建器(相当于God类)
*
* @return {@link StudentBuilder}
*/
public static Student.StudentBuilder builder() {
// 构造一个StudentBuilder对象
return new Student.StudentBuilder();
}
/**
* 学生构建器(相当于StudentBuilder及其实现类StudentActualBuilder)
*
* @author 第七人格
* @date 2020/12/02
*/
public static class StudentBuilder {
private String name;
private int age;
StudentBuilder() {
}
public Student.StudentBuilder name(String name) {
this.name = name;
// 返回自身(StudentBuilder),以便链式调用
return this;
}
public Student.StudentBuilder age(int age) {
this.age = age;
// 返回自身(StudentBuilder),以便链式调用
return this;
}
/**
* 构建
*
* @return {@link Student}
*/
public Student build() {
// 构造一个Student对象,其中的属性直接从外部传入
return new Student(this.name, this.age);
}
@Override
public String toString() {
return "Student.StudentBuilder(name=" + this.name + ", age=" + this.age + ")";
}
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试类
public class Test {
public static void main(String[] args) {
Student student = new Student.StudentBuilder().age(28).name("第七人格").build();
System.out.println(student);
}
}
结果
小技巧
建造者的代码量看似很简单,但是因为平常一般用的少,链式调用也不太符合平常编码的思考方式,所以不看参考代码直接撸码,对一些同学来说还是很有难度的。如果你有使用lombok,一个注解就可以解决问题,在对应类上增加注解@Builder
最终代码简化如下(查看class文件,可以发现最后的源码和内部内实现方式一致):
@Builder
public class Student {
/**
* 姓名
*/
private String name;
/**
* 年龄
*/
private int age;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试类
public class Test {
public static void main(String[] args) {
Student student = new Student.StudentBuilder().age(28).name("第七人格").build();
System.out.println(student);
}
}
结果