学了这个,三歪再也不想写各种setter了

简介: 今天又来给大家吹一下逼。三歪在公司里边也看了不少的系统了,看到结构清晰、代码清晰的系统时会赞叹能写出这种代码的人是真的牛逼。看到乱七八糟的代码又不写注释的时候也会吐槽:“这写的是啥啊”

今天又来给大家吹一下逼。

三歪在公司里边也看了不少的系统了,看到结构清晰、代码清晰的系统时会赞叹能写出这种代码的人是真的牛逼。看到乱七八糟的代码又不写注释的时候也会吐槽:“这写的是啥啊”

多看别人的代码,总会有不同的发现和体会。

最近看别人项目的时候,发现会有这种写法:

MessageTask task = MessageTask.builder()
  .content("关注 Java3y 吧 >> ")
  .messageId(String.valueOf(ThreadLocalRandom.current().nextLong()))
  .taskId("3y")
  .taskName("一起来玩")
  .build();
System.out.println(task.toString());

看代码其实很容易看出它在干嘛,就是在创建MessageTask这个对象。

平时我们初学的时候,如果要创建这个对象会怎么写?一般会有两种方法:

  1. 将属性配在构造函数上,然后直接调构造器,传入参数
  2. 调用多个set方法
// 构造器传入属性
MessageTask messageTask = new MessageTask("3y", "关注 Java3y 吧 >>", 
                                          String.valueOf(ThreadLocalRandom.current().nextLong()), "一起来玩");
// 调用各种的set方法
MessageTask messageTask = new MessageTask();
messageTask.setTaskId("3y");
messageTask.setContent("关注 Java3y 吧 >>");
messageTask.setMessageId(String.valueOf(ThreadLocalRandom.current().nextLong()));
messageTask.setTaskName("一起来玩");

日常使用的话,应该是多次调用set方法比较多的(应该都是这样的吧)。

从代码层面上,构造器传参的代码是最简短的,但在现实层面上我们很难每次都可以通过构造器传参的方式去完成对象的创建(更多的时候每个对象的属性都是不一致的)。

构造器的代码看起来非常短,但阅读起来不太友好(我得去看每个参数是什么意思);

set方法写起来不太方便,如果对象的属性较多,也会有一大串的set代码。

而文章最开始的builer链式调用就很舒服,我一看这代码就知道这肯定是哪种我不知道的设计模式。

于是我一查,原来这就叫做建造者模式


怎么实现建造者模式?


建造者模式更多的是写法上的不同,从代码结构层面上其实没有很大的区别,只是看起来会更清爽一些。

那怎么实现建造者模式呢?其实也非常简单:

  • 在domain类上创建一个静态内部类 Builder,Builder拥有domain所有的属性
  • 在domain类上创建一个private的构造函数,参数为Builder类型,里边将Builder的属性赋值给domain的属性
  • 在Builder内部类创建domain属性的赋值方法,返回值是Builder
  • Builder内部类创建一个build方法,返回domain实例

下面我们来实现一下吧,首先创建一个静态内部类Builder,并且内部类Builder拥有domain的所有属性:

public class MessageTask {
    private String taskId;
    private String content;
    private String messageId;
    private String taskName;
   // 创建内部类
    public static class Builder{
        private String taskId;
        private String content;
        private String messageId;
        private String taskName;
    }
}

在domain类上创建一个private的构造函数,参数为Builder类型,里边是将Builder的属性赋值给domain的属性:

public class MessageTask {
    private String taskId;
    private String content;
    private String messageId;
    private String taskName;
   // 增加private构造函数
    private MessageTask(Builder builder) {
        this.taskId = builder.taskId;
        this.content = builder.content;
        this.messageId = builder.messageId;
        this.taskName = builder.taskName;
    }
    public static class Builder{
        private String taskId;
        private String content;
        private String messageId;
        private String taskName;
    }
}

在Builder内部类创建domain属性的赋值方法,返回值是Builder

public class MessageTask {
    private String taskId;
    private String content;
    private String messageId;
    private String taskName;
    private MessageTask(Builder builder) {
        this.taskId = builder.taskId;
        this.content = builder.content;
        this.messageId = builder.messageId;
        this.taskName = builder.taskName;
    }
    public static class Builder{
        private String taskId;
        private String content;
        private String messageId;
        private String taskName;
        // 赋值属性的方法(返回的是Builder)
        public Builder setTaskId(String taskId) {
            this.taskId = taskId;
            return this;
        }
        public Builder setContent(String content) {
            this.content = content;
            return this;
        }
        public Builder setMessageId(String messageId) {
            this.messageId = messageId;
            return this;
        }
        public Builder setTaskName(String taskName) {
            this.taskName = taskName;
            return this;
        }
    }
}

在Builder内部类创建一个builde方法,返回domain实例

public class MessageTask {
    private String taskId;
    private String content;
    private String messageId;
    private String taskName;
    private MessageTask(Builder builder) {
        this.taskId = builder.taskId;
        this.content = builder.content;
        this.messageId = builder.messageId;
        this.taskName = builder.taskName;
    }
    public static class Builder{
        private String taskId;
        private String content;
        private String messageId;
        private String taskName;
        public Builder setTaskId(String taskId) {
            this.taskId = taskId;
            return this;
        }
        public Builder setContent(String content) {
            this.content = content;
            return this;
        }
        public Builder setMessageId(String messageId) {
            this.messageId = messageId;
            return this;
        }
        public Builder setTaskName(String taskName) {
            this.taskName = taskName;
            return this;
        }
    // 创建build方法,返回实例
        public MessageTask build() {
            return new MessageTask(this);
        }
    }
}

使用方式:先创建Builder对象,然后用Builder去赋值,最后再调用build()返回真正的实例:

MessageTask.Builder builder = new MessageTask.Builder();
MessageTask task = builder.setContent("关注 Java3y 吧 >>")
  .setTaskId("3y")
  .setTaskName("一起来玩")
  .setMessageId(String.valueOf(ThreadLocalRandom.current().nextLong()))
  .build();


借助工具使用建造者模式


从使用的角度感觉是好用了,代码也清爽了。其实可以发现的是,我们都把逻辑写到了Domain类上,这写起来肯定是需要花时间的。

我第一印象是:这种这么通用的东西,肯定是有可以一键生成的方法的,于是我瞄准了Lombok

果不其然,我们如果使用了Lombok后,在类上加上一个注解@Builder就可以使用建造者模式的代码了,非常方便

@Builder
@Data
public class MessageTask {
    private String taskId;
    private String content;
    private String messageId;
    private String taskName;
}

又想了一下,IDEA这么强大,感觉IDEA也有办法去生成Builder,也果不其然:

35.jpg

做个小调查:

  • 如果你之前已经了解过了建造者模式,你在项目中有用吗?
  • 如果你看了这篇文章才了解到了建造者模式,你在以后会用吗?

我个人是看团队的代码风格的,如果原有已经是各种set去构造对象,那我就不会再修改了。如果是新写的业务,会使用建造者模式。


目录
相关文章
|
6月前
|
人工智能 编解码 物联网
设计师集体破防!UNO:字节跳动创新AI图像生成框架,多个参考主体同框生成,位置/材质/光影完美对齐
UNO是字节跳动开发的AI图像生成框架,通过渐进式跨模态对齐和通用旋转位置嵌入技术,解决了多主体场景下的生成一致性问题。该框架支持单主体特征保持与多主体组合生成,在虚拟试穿、产品设计等领域展现强大泛化能力。
340 4
设计师集体破防!UNO:字节跳动创新AI图像生成框架,多个参考主体同框生成,位置/材质/光影完美对齐
|
9月前
|
人工智能 自然语言处理 算法
随机的暴力美学蒙特卡洛方法 | python小知识
蒙特卡洛方法是一种基于随机采样的计算算法,广泛应用于物理学、金融、工程等领域。它通过重复随机采样来解决复杂问题,尤其适用于难以用解析方法求解的情况。该方法起源于二战期间的曼哈顿计划,由斯坦尼斯拉夫·乌拉姆等人提出。核心思想是通过大量随机样本来近似真实结果,如估算π值的经典示例。蒙特卡洛树搜索(MCTS)是其高级应用,常用于游戏AI和决策优化。Python中可通过简单代码实现蒙特卡洛方法,展示其在文本生成等领域的潜力。随着计算能力提升,蒙特卡洛方法的应用范围不断扩大,成为处理不确定性和复杂系统的重要工具。
416 21
|
机器学习/深度学习 缓存 自然语言处理
一文揭秘|预训练一个72b模型需要多久?
本文讲述评估和量化训练大规模语言模型,尤其是Qwen2-72B模型,所需的时间、资源和计算能力。
718 12
|
存储 弹性计算 Docker
深入探讨Docker的主要功能及其影响力
【8月更文挑战第24天】
308 0
|
机器学习/深度学习 计算机视觉
YOLOv10实战:SPPF原创自研 | SPPF_attention,重新设计加入注意力机制 | NEU-DET为案列进行展开
【7月更文挑战第1天】 优点:为了利用不同的池化核尺寸提取特征的方式可以获得更多的特征信息,提高网络的识别精度; 如何优化:在此基础上加入注意力机制,能够在不同尺度上更好的、更多的获取特征信息,从而获取全局视角信息并减轻不同尺度大小所带来的影响; SPPF_attention,重新设计加入注意力机制 ,在NEU-DEU任务中mAP50从0.683提升至0.703;
1151 3
|
分布式计算 Hadoop 关系型数据库
Sqoop与Spark的协作:高性能数据处理
Sqoop与Spark的协作:高性能数据处理
Sqoop与Spark的协作:高性能数据处理
|
芯片
模电练习题-多路信号发生器(题目)
模电练习题-多路信号发生器(题目)
744 2
模电练习题-多路信号发生器(题目)
|
SQL Java API
Flink实战(六) - Table API & SQL编程
Apache Flink具有两个关系型API Table API SQL 用于统一流和批处理 Table API是Scala和Java语言集成查询API,可以非常直观的方式组合来自关系算子的查询(e.g. 选择,过滤和连接).
2890 0
|
Java 程序员 Apache
免费申请和使用IntelliJ IDEA商业版License指南
IntelliJ IDEA是广受Java开发者喜爱的工具,其商业版的价格十分昂贵,现在有机会免费获取IntelliJ IDEA的正版License,您是否心动呢?咱们一起行动吧。
664 0
免费申请和使用IntelliJ IDEA商业版License指南