楔子
小七在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;}@Overridepublic 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();@Overridepublic void buildName(String name) {student.setName(name);}@Overridepublic void buildAge(int age) {student.setAge(age);}@Overridepublic 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 {@Testpublic 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);}@Overridepublic String toString() {return "Student.StudentBuilder(name=" + this.name + ", age=" + this.age + ")";}}@Overridepublic 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、内部类中设置属性的时候,返回自身,以便链式调用
面对对象面对君,不负代码不负卿