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

news2024/9/21 12:30:31

文章目录

  • 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.Default
    private Integer age = 18;
    private String phone;
    private String email;
}

在这里插入图片描述
在这里插入图片描述
在反编译的代码中可以看到,如果没有给age赋值,则会取默认值。


2.5.lombok简化建造者的缺陷

虽然我们可以使用lombok来简化建造者,但我们现在抛开实现回来想一想使用建造者模式的目的是什么?
我们希望在创建对象的时候,有这么一个内聚的对象,能够自由的给对象组装不同的字段的能力,同时希望在字段与字段之间能够形成一点的联动、校验,满足一些特定的要求。

而使用lombok来生成的建造者,能够满足这样的要求吗?
显然,它只是提供了一个自动生成建造者的能力,而没有关心生成这个建造者的目的是什么,所以它并不能满足我们使用建造者的需要。
如果说只是为了组装构建对象而编写一个建造者,相对于new出对象之后,再一个一个set字段值就没有什么优势了,后者编写起来还更加简单。

3.总结

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


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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

串的基本操作(数据结构)

串的基本操作 #include <stdlib.h> #include <iostream> #include <stdio.h> #define MaxSize 255typedef struct{char ch[MaxSize];int length; }SString;//初始化 SString InitStr(SString &S){S.length0;return S; } //为了方便计算&#xff0c;串的…

Java架构师部署架构设计

目录 1 导学2 部署架构设计和部署架构图3 实战整体部署架构设计4 节点部署说明列表5 总结1 导学 本章的主要内容是整体架构设计的核心之一,部署架构设计相关的一些知识落到项目上,就是系统系统的部署架构设计。在本章学习里面我们可以去去学习整部署架构构,主要是部署架构设…

大模型分布式训练并行技术(四)-张量并行

linkj 近年来&#xff0c;随着Transformer、MOE架构的提出&#xff0c;使得深度学习模型轻松突破上万亿规模参数&#xff0c;传统的单机单卡模式已经无法满足超大模型进行训练的要求。因此&#xff0c;我们需要基于单机多卡、甚至是多机多卡进行分布式大模型的训练。 而利用AI集…

基于 ACK Fluid 的混合云优化数据访问(二):搭建弹性计算实例与第三方存储的桥梁

作者&#xff1a;车漾 前文回顾&#xff1a; 本系列将介绍如何基于 ACK Fluid 支持和优化混合云的数据访问场景&#xff0c;相关文章请参考&#xff1a; 基于 ACK Fluid 的混合云优化数据访问&#xff08;一&#xff09;&#xff1a;场景与架构 在前文《场景与架构》中&…

Springboot使用RestTemplate调用第三方接口

配置 新建一个RestTemplate的配置类&#xff0c;如下&#xff1a; /*** RestTemplate配置项*/ Configuration public class RestTemplateConfig {Beanpublic RestTemplate restTemplate() {return new RestTemplate();}} 在controller中引入RestTemplate&#xff0c;如下&am…

PHP 车辆租赁系统mysql数据库web结构apache计算机软件工程网页wamp

一、源码特点 PHP 车辆租赁系统是一套完善的web设计系统&#xff0c;对理解php编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。 php 车辆租赁管理系统1 代码 https://download.csdn.net/download/qq_41221322/88414512…

软信天成:AI驱动,化解企业数据的隐私之痛

近年来&#xff0c;在全面数字化的背景下&#xff0c;不断加剧的数据丢失和爆炸式的个人数据增长让数据隐私的需求变得空前迫切。为解决个人、企业等敏感数据被滥用的现状&#xff0c;各地监管机构开始逐渐完善区域内数据隐私法律法规。从《欧盟通用数据保护条例》&#xff08;…

档案宝档案管理系统在微信小程序上线了!

随着信息时代的到来&#xff0c;企业和组织面临着越来越多的信息和数据需要管理。而档案管理作为一项重要的任务&#xff0c;对于企业的运营和决策起着至关重要的作用。为了满足用户的需求&#xff0c;我们很高兴地宣布&#xff0c;档案宝档案管理系统已经在微信小程序上线了&a…

git clone项目报错文件名过长

1.最近从git上clone项目时&#xff0c;有时会遇到报错&#xff0c;报出Filename too long错误。 Filename too long fatal: unable to checkout working tree warning: Clone succeeded, but checkout failed. 2.原因 Windows支持最长260字符的文件名&#xff08;包括其路径在…

Linux-文件管理命令

绝对路径&#xff1a;从根目录开始描述的路径 pwd输入即为绝对路径&#xff0c; 开头一定是“/”&#xff0c;因为一定是从根目录开始走 相对路径&#xff1a;从当前路径开始描述的路径&#xff0c;开头不一定是“/”&#xff0c;因为不一定是从根目录开始走的 .:是当前目录 。…

无人值守配电室变电所运维解决方案

随着电力系统数字化、智能化的不断发展&#xff0c;无人值守配电室变电所已经成为一种趋势。为了确保变电所的安全稳定运行&#xff0c;本文提出了一种无人值守配电室变电所运维解决方案。 一、背景介绍 力安科技电易云无人值守配电室变电所是指通过远程监控和智能化电力数…

虚拟机软件Parallels Desktop 19 mac功能介绍

Parallels Desktop 19 mac是一款虚拟机软件&#xff0c;它允许用户在Mac电脑上同时运行Windows、Linux和其他操作系统。Parallels Desktop提供了直观易用的界面&#xff0c;使用户可以轻松创建、配置和管理虚拟机。 PD19虚拟机软件具有快速启动和关闭虚拟机的能力&#xff0c;让…

实盘股票杠杆平台排名报告:五大排名和评估一览

本报告旨在根据公开信息和用户反馈&#xff0c;对股票配资公司的五大排名进行深入分析。这些排名包括尚红网、倍悦网、兴盛网、诚利和、嘉正网、广瑞网、富灯网、天创网、恒正网、创通网。 一、尚红网 尚红网在股票配资公司中一直享有盛誉&#xff0c;其资金雄厚&#xff0c;…

Linux操作系统常见指令理解(1)

Linux是一种开源的操作系统&#xff0c;常用于服务器和嵌入式设备。以下是一些常见的Linux指令及其详解&#xff1a; ls&#xff1a;列出目录中的文件和子目录。 cd&#xff1a;改变当前工作目录。 pwd&#xff1a;显示当前工作目录的路径。 mkdir&#xff1a;创建一个新目录。…

告别繁琐开发,轻松玩转无代码!

导语&#xff1a;随着科技的快速发展&#xff0c;无代码开发已成为越来越多人的首选。告别繁琐的开发流程&#xff0c;无代码将让你轻松驾驭各种项目需求&#xff01;本文将带您了解无代码的魅力&#xff0c;以及如何玩转无代码&#xff0c;为您的项目插上腾飞的翅膀&#xff0…

前端-Vue-开发指南

VueJS 开源文档 拉入vscode安装node.js安装vue脚手架components : 组件router&#xff1a;路由创建新组建 &#xff1a;assets&#xff1a; 系统图片存放地址main.js&#xff1a; vue脚手架对象存放地 &#xff08;新的包要放在里面&#xff09;属性 computedslot 插槽error St…

基于Effect的组件设计 | 京东云技术团队

Effect的概念起源 从输入输出的角度理解Effect https://link.excalidraw.com/p/readonly/KXAy7d2DlnkM8X1yps6L 编程中的Effect起源于函数式编程中纯函数的概念 纯函数是指在相同的输入下&#xff0c;总是产生相同的输出&#xff0c;并且没有任何副作用(side effect)的函数。…

使用Perl脚本编写爬虫程序的一些技术问题解答

网络爬虫是一种强大的工具&#xff0c;用于从互联网上收集和提取数据。Perl 作为一种功能强大的脚本语言&#xff0c;提供了丰富的工具和库&#xff0c;使得编写的爬虫程序变得简单而灵活。在使用的过程中大家会遇到一些问题&#xff0c;本文将通过问答方式&#xff0c;解答一些…

循环神经网络时间序列预测

循环神经网络时间序列预测 循环神经网络用于时间序列预测比较好&#xff0c;先使用股票价格数据集来理解和掌握该算法。 数据来源 数据要先在Tushare注册登录一下&#xff0c;获取足够的积分 注册完之后有100积分&#xff0c;然后个人信息完善一下获取20积分&#xff0c;然后…

网络通信三要素

三要素概述 IP地址&#xff1a;设备在网络中的地址&#xff0c;是唯一的标识。 端口&#xff1a;应用程序在设备中唯一的标识。 协议: 数据在网络中传输的规则&#xff0c;常见的协议有UDP协议和TCP协议。 网络通信过程 A程序通过IP和端口找到B程序&#xff0c;再互相发…