【设计模式】使用建造者模式组装对象并加入自定义校验

文章目录

  • 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.总结

本篇描述了如何实践建造者模式,同时也聊到了为什么要使用建造者模式。
简单的说,就是建造者模式提供了一种能力,让我们在自由的组装构建对象的同时,给到一定的约束。而这样的能力往往是用在框架代码中的,由架构提供建造者,开发者按照建造者的约束去相对自由的构建需要的对象。
在业务代码中,如果我们并没有太多这样的校验的要求,只是希望创建一个对象,直接newset又何乐不为呢?


最后,如果觉得本文有所帮助的话,可以点赞收藏哦~
如果您有不同的见解,也可以留言一同讨论,共同学习进步!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/132303.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何在 Spring Boot 中进行数据备份

在Spring Boot中进行数据备份 数据备份是确保数据安全性和可恢复性的关键任务之一。Spring Boot提供了多种方法来执行数据备份,无论是定期备份数据库,还是将数据导出到外部存储。本文将介绍在Spring Boot应用程序中进行数据备份的不同方法。 方法1: 使用…

K8S:HPA pod水平自动伸缩

文章目录 一.HPA概念1.什么是HPA2.HPA原理 二.部署 metrics-server1.node节点上传镜像包2.master节点安装metrics-server 三.部署 HPA1.所有节点安装镜像2.master创建测试的 Pod 资源3.创建 HPA 控制器4.创建测试客户端容器5.弹性缩容 四.扩展1.资源限制 - Pod①资源限制的原理…

干货|小白也能自制电子相册赶紧码住~

你是否想拥有一个独一无二的电子相册,却又苦于不知道如何下手?今天教你一个简单的方法,即使你是小白,也能轻松自制电子相册! 一、选择合适的工具 首先,你需要选择一个合适的工具来制作电子相册。有很多工具…

ubuntu20.04 nerf Instant-ngp

Instant-ngp linux ubuntu 20.04 GPU RTX3050Ti Instant-ngp官方文档地址 https://github.com/NVlabs/instant-ngp 参考链接Instant-ngp linux部署及使用 - 简书 Ubuntu20.04复现instant-ngp,自建数据集,导出mesh_XINYU W的博客-CSDN博客 步骤 安装基…

数据库基础(一)

数据库面试基础 注,本文章内容主要来自于JAVAGUIDE,只是结合网上资料和自己的知识缺陷进行一点补充,需要准备面试的请访问官方网址。 一、范式 参考链接 函数依赖:一张表中,确定X则必定能确定Y,则X->…

[GXYCTF2019]Ping Ping Ping - RCE(空格、关键字绕过[3种方式])

[GXYCTF2019]Ping Ping Ping 1 解题流程1.1 小试牛刀1.2 三种解法1.2.1 解法一:变量定义拼接绕过1.2.2 解法二:base64编码绕过1.2.3 解法三:内联执行绕过2 思考总结1 解题流程 1.1 小试牛刀 1、提示?ip,结合题目名称,我们直接输入?ip=127.0.0.1 PING 127.0.0.1 (127.…

【Spring AOP】统一用户登录校验

统一用户登录校验 一. 使用拦截器实现统一用户登录校验1. 自定义拦截器2. 将拦截器加入到系统配置 二. 拦截器实现原理三. 扩展:统一访问前缀添加 一. 使用拦截器实现统一用户登录校验 Spring 中提供了具体的实现拦截器:HandlerInterceptor,…

数据结构学习笔记——数据结构概论

目录 一、数据与数据元素二、数据类型和抽象数据类型三、数据结构的定义(一)逻辑结构(二)存储结构(物理结构)1、顺序存储结构2、链式存储结构3、索引存储结构4、散列存储结构 (三)数…

基于Cl2/BCl3电感偶联等离子体的氮化镓干蚀特性

引言 氮化镓(GaN)具有六方纤锌矿结构,直接带隙约为3.4eV,目前已成为实现蓝光发光二极管(led)的主导材料。由于GaN的高化学稳定性,在室温下用湿法化学蚀刻来蚀刻或图案化GaN是非常困难的。与湿法蚀刻技术相比,干法蚀刻技术可以提供…

【计算机网络】——前言计算机网络发展的历程概述

主页点击直达:个人主页 我的小仓库:代码仓库 C语言偷着笑:C语言专栏 数据结构挨打小记:初阶数据结构专栏 Linux被操作记:Linux专栏 LeetCode刷题掉发记:LeetCode刷题 算法:算法专栏 C头…

wins打开ftp服务,跳转到浏览器解决方式

问题: 在wins的资源管理器中输入 ftp://服务器ip的时候,会突然跳转到浏览器中 百度上的方法归纳汇总 解决方法: 百度上最多的方式,但是我电脑试了不行 启动 InternetExplorer 在 菜单栏 选择 工具 -> Internet 选项 -> 高级 -> 勾选 启用 …

如何查找文献,如何阅读文献

一、高效查找需要阅读的文献 1、首先进入知网的高级检索页,点击“学术期刊”,你会看到“来源类别”选择,在这个里选择北核和C刊 2、在检索结果页选择一篇自己感兴趣的文章,点击篇名进入文章详情页,下拉可看到核心文献…