网站建设广告平台推广,wordpress 修改首页,做百度手机网站优化点,seo短视频网页入口引流网址文章目录 1.前言1.1.创建对象时的痛点 2.建造者模式2.1 被建造类准备2.2.建造者类实现2.3.构建对象测试2.4.使用lombok简化建造者2.5.lombok简化建造者的缺陷 3.总结 1.前言
在我刚入行不久的时候就听说过建造者模式这种设计模式#xff0c;当时只知道是用来组装对象#xf… 文章目录 1.前言1.1.创建对象时的痛点 2.建造者模式2.1 被建造类准备2.2.建造者类实现2.3.构建对象测试2.4.使用lombok简化建造者2.5.lombok简化建造者的缺陷 3.总结 1.前言
在我刚入行不久的时候就听说过建造者模式这种设计模式当时只知道是用来组装对象使用起来和先new出对象再一个一个的set字段值也差不多没有去深究这种设计模式。后来随着在业务中写的代码越来越多同时也去查阅了一些资料慢慢的对这两者的区别有了一点理解。于是对为什么要用建造者模式以及相对于直接set的优劣势做一个简单的梳理。
1.1.创建对象时的痛点
在不同的业务流程需求当中在创建了对象之后对于对象各个字段的赋值可能还会有一些额外的要求这里举几个典型的例子
对象中的某几个字段不能为空或需要满足一定的校验要求。字段与字段之间有联动例如填写了电话号码就不用填邮箱地址但两者不能都为空某些字段值只能创建时赋值一次赋值后就不可变了
对于上面的这些需求直接通过set不是太好实现的可以有这样一些思路
在类的构造函数中写上校验、联动等逻辑借助一下其他的方法比如写一个静态工具方法传入参数生成对象并返回
但是问题又来了假如现在有好几个业务流程每个流程当中需要set不同的字段值那就需要我们 针对每个不同的需求都创建对应的方法或构造函数 来生成不同的对象。 当这么做的时候后续的迭代拓展会让方法越来越多拓展变动越来越困难。 针对对象的字段组装需求上的痛点可以使用建造者模式来处理功能疑更易维护且更加内聚。
2.建造者模式
接下来就用建造者模式实现一下上面的需求假设我们现在有一个Person类型包含了name,age,phone,email字段其中
name和age不能为空phone和email不能同时为空所有的属性在创建时初始化创建完成后不能再修改
2.1 被建造类准备
我们先创建一个Person类并且只提供get方法满足不能修改的需求必要时还可以给每个字段加上final修饰
import lombok.Getter;Getter
public class PersonModel {private String name;private Integer age;private String phone;private String email;
}接下来就需要提供一个构造函数用于完成初始化工作这个构造函数接收一个Builder对象作为参数这个对象就是我们接下来要说的建造者对象。
import lombok.Getter;Getter
public class PersonModel {private String name;private Integer age;private String phone;private String email;// 用于初始化的构造函数public PersonModel(Builder builder) {this.name builder.name;this.age builder.age;this.phone builder.phone;this.email builder.email;}
}2.2.建造者类实现
建造者类拥有与被建造的类相同的属性同时给每个属性提供一个同名方法这个方法用于给字段赋值并且会返回建造者对象方便链式调用最后提供一个build方法用于生成被建造的对象。 建造者类可以是一个独立的类也可以是是被建造类的静态内部类静态内部类的方式相对来说更加内聚这里采用这种方式完整的代码如下所示。
Getter
public class PersonModel {private String name;private Integer age;private String phone;private String email;// 用于初始化的构造函数public PersonModel(Builder builder) {this.name builder.name;this.age builder.age;this.phone builder.phone;this.email builder.email;}// 建造者类public static class Builder {private String name;private Integer age;private String phone;private String email;public Builder name(String name) {this.name name;return this;}public Builder age(Integer age) {this.age age;return this;}public Builder phone(String phone) {this.phone phone;return this;}public Builder email(String email) {this.email email;return this;}public PersonModel build() {return new PersonModel(this);}}
}我们之前提到的参数的验证就可以写在build方法中
public PersonModel build() {if (StringUtils.isBlank(name)) {throw new RuntimeException(姓名不能为空);}if (age null) {throw new RuntimeException(年龄不能为空);}if (StringUtils.isBlank(phone) StringUtils.isBlank(email)) {throw new RuntimeException(联系方式和邮箱不能同时为空);}return new PersonModel(this);
}2.3.构建对象测试
完成之后就可以通过建造者类来组装和构建对象了客户端在使用的时候可以灵活的选择要给哪些字段赋值赋什么值例如
Test
public void testBuildPerson() {PersonModel personModel new PersonModel.Builder().name(挥之以墨).age(18).phone(123456789).build();
}此时就会构建出一个personModel对象而当我们传入的参数不符合要求时就会根据我们build中的写法抛出异常给出相应的提示。
Test
public void testBuildPerson() {PersonModel personModel new PersonModel.Builder().name(挥之以墨).age(18).build();
}2.4.使用lombok简化建造者
我们可以从上面看到建造者类的编写还是比较繁琐的如果没有build函数中的各种操作需求可以考虑直接使用lombok来生成建造者类只需要加上一个Builder注解即可。
import lombok.Builder;
import lombok.Getter;Getter
Builder
public class PersonModelPure {private String name;private Integer age;private String phone;private String email;
}编译后我们通过IDE打开.class文件可以看到反编译的代码如下生成了一个建造者类
需要注意的是在使用lombok来简化建造者之后我们在源代码中由于没有建造者的源码所以无法给Person对象中的字段赋予默认值直接在Person类的字段上赋值是无效的会被构造方法覆盖。此时需要使用Builder.Default来标记有默认值的字段
Getter
Builder
public class PersonModelPure {private String name;Builder.Defaultprivate Integer age 18;private String phone;private String email;
}在反编译的代码中可以看到如果没有给age赋值则会取默认值。 2.5.lombok简化建造者的缺陷
虽然我们可以使用lombok来简化建造者但我们现在抛开实现回来想一想使用建造者模式的目的是什么 我们希望在创建对象的时候有这么一个内聚的对象能够自由的给对象组装不同的字段的能力同时希望在字段与字段之间能够形成一点的联动、校验满足一些特定的要求。
而使用lombok来生成的建造者能够满足这样的要求吗 显然它只是提供了一个自动生成建造者的能力而没有关心生成这个建造者的目的是什么所以它并不能满足我们使用建造者的需要。 如果说只是为了组装构建对象而编写一个建造者相对于new出对象之后再一个一个set字段值就没有什么优势了后者编写起来还更加简单。
3.总结
本篇描述了如何实践建造者模式同时也聊到了为什么要使用建造者模式。 简单的说就是建造者模式提供了一种能力让我们在自由的组装构建对象的同时给到一定的约束。而这样的能力往往是用在框架代码中的由架构提供建造者开发者按照建造者的约束去相对自由的构建需要的对象。 在业务代码中如果我们并没有太多这样的校验的要求只是希望创建一个对象直接new并set又何乐不为呢 最后如果觉得本文有所帮助的话可以点赞收藏哦~ 如果您有不同的见解也可以留言一同讨论共同学习进步