楔子
小七在2019年的时候,就想写一个关于设计模式的专栏,但是最终却半途而废了。粗略一想,如果做完一件事要100分钟,小七用3分钟热情做的事,最少也能完成10件事情了。所以这一次,一定要把他做完,fighting!
需求背景
以以前小七做的一个政务系统为例,为了符合国标,数据库表需要设计很多字段,大概有100多个。每次new这个实体的时候,都会调用大量的set方法,关键是这100个字段基本不会变,但是他们的组合却经常变,弄得开发的小伙伴们苦不堪言,于是前辈们就重载了很多的构造方法,结果构造方法也爆炸了,导致新来的后浪们差点直接被拍死在了沙滩上。
为了简化代码,咱们这一次就定义一个Student类,里面只包含name和age。
分析设计
因为这个对象的属性很多,且组合方式很自由,如果使用经典的new-set方式,代码大概如下:
BigObject bigObject = new BigObject();
bigObject.setO1("");
bigObject.setO2("");
bigObject.setO3("");
bigObject.setO4("");
bigObject.setO5("");
bigObject.setO6("");
bigObject.setO7("");
bigObject.setO8("");
bigObject.setO9("");
bigObject.setO10("");
...
bigObject.setO100("");
看起来并不直观。
如果每一个组合就重载一个构造方法,也会产生很多构造方法,并且语义不明,新来的小伙伴会一脸懵逼。
但是如果我们能够抽象一下产品的构建过程,具体建造者类继承自抽象建造者类,实现具体的构建逻辑。指挥者类负责调用具体建造者类的构建方法,完成产品的构建。这样就可以降低客户端代码的复杂度,提高代码的可维护性。
定义 | 类名 |
---|---|
产品类 | Student |
抽象建造者类 | StudentBuilder |
具体建造者类 | StudentActualBuilder |
指挥者类 | Commander |
标准建造者模式
UML图
根据分析设计,我们可以先画一个简单的UML图,后面通过UML图编码
模块名称
builder.demo01
模块地址
https://gitee.com/diqirenge/design-pattern/tree/master/src/main/java/com/run2code/design/creational/builder/demo01
模块描述
经典模式代码示例
代码实现
1、定义产品类
/**
* 定义产品类
* 关注公众号【奔跑的码畜】,一起进步不迷路
*
* @author 第七人格
* @date 2023/11/20
*/
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 +
'}';
}
}
2、定义抽象建造者类
/**
* 定义抽象建造者类
* 关注公众号【奔跑的码畜】,一起进步不迷路
*
* @author 第七人格
* @date 2023/11/20
*/
public abstract class StudentBuilder {
public abstract void buildName(String name);
public abstract void buildAge(int age);
public abstract Student makeStudent();
}
3、定义具体建造者类
/**
* 定义具体建造者类
* 关注公众号【奔跑的码畜】,一起进步不迷路
*
* @author 第七人格
* @date 2023/11/20
*/
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;
}
}
4、定义指挥者类
/**
* 定义指挥者类
* 关注公众号【奔跑的码畜】,一起进步不迷路
*
* @author 第七人格
* @date 2023/11/20
*/
public class Commander {
/**
* 注入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();
}
}
5、测试
public class BuilderStudentBuilderTest {
@Test
public void testBuild_01() {
System.out.println("==========标准建造者模式开始==========");
StudentActualBuilder studentActualBuilder = new StudentActualBuilder();
Commander commander = new Commander();
commander.setStudentBuilder(studentActualBuilder);
// 客户端使用指挥者类创建产品对象,这样可以降低客户端代码的复杂度,提高代码的可维护性。
Student student = commander.makeStudent("第七人格", 18);
System.out.println(student);
System.out.println("==========标准建造者模式结束==========");
}
}
6、测试结果
==========标准建造者模式开始==========
Student{name='第七人格', age=18}
==========标准建造者模式结束==========
实现要点
定义产品类:产品类是最终要构建的对象,包含多个属性和方法。
定义抽象建造者类:抽象建造者类定义了产品的构建过程,包括各个部分的构建方法和返回最终产品的方法。
定义具体建造者类:具体建造者类继承自抽象建造者类,实现具体的构建逻辑。
定义指挥者类:指挥者类负责调用具体建造者类的构建方法,完成产品的构建。
用过StringBuilder的我们知道,StringBuilder有个append方法,我们学着StringBuilder将上面的代码,改为链式调用。
链式调用模式
URL图
模块名称
builder.demo02
模块地址
https://gitee.com/diqirenge/design-pattern/tree/master/src/main/java/com/run2code/design/creational/builder/demo02
模块描述
建造者-链式调用
代码实现
/**
* 链式调用建造者示例
* 关注公众号【奔跑的码畜】,一起进步不迷路
*
* @author 第七人格
* @date 2023/11/20
*/
public class Student02Builder {
/**
* 姓名
*/
private String name;
/**
* 年龄
*/
private int age;
/**
* 学生类的构造函数
*
* @param name 的名字
* @param age 年龄
*/
Student02Builder(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 构建器(本质上就是指挥者Commander)
*
* @return {@link StudentBuilder}
*/
public static Student02Builder.StudentBuilder builder() {
// 构造一个StudentBuilder对象
return new Student02Builder.StudentBuilder();
}
/**
* 学生构建器(相当于StudentBuilder及其实现类StudentActualBuilder)
*
* @author 第七人格
* @date 2020/12/02
*/
public static class StudentBuilder {
private String name;
private int age;
public StudentBuilder() {
}
public Student02Builder.StudentBuilder name(String name) {
this.name = name;
// 返回自身(StudentBuilder),以便链式调用
return this;
}
public Student02Builder.StudentBuilder age(int age) {
this.age = age;
// 返回自身(StudentBuilder),以便链式调用
return this;
}
/**
* 构建
*
* @return {@link Student02Builder}
*/
public Student02Builder build() {
// 构造一个Student对象,其中的属性直接从外部传入
return new Student02Builder(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 +
'}';
}
}
测试
@Test
public void testBuild_02() {
System.out.println("==========工作中常用-建造者模式开始==========");
System.out.println(Student02Builder.builder()
.age(18)
.name("第七人格")
.build()
);
System.out.println("==========工作中常用-建造者模式开始==========");
}
测试结果
==========链式调用-建造者模式开始==========
Student{name='第七人格', age=18}
==========链式调用-建造者模式开始==========
实现要点
1、使用静态方法替换指挥者Commander
public static Student02Builder.StudentBuilder builder() {
// 构造一个StudentBuilder对象
return new Student02Builder.StudentBuilder();
}
2、使用内部类替换StudentBuilder及StudentActualBuilder
3、内部类中设置属性的时候,返回自身,以便链式调用
面对对象面对君,不负代码不负卿