学了这个,三歪再也不想写各种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去构造对象,那我就不会再修改了。如果是新写的业务,会使用建造者模式。


目录
相关文章
|
7月前
|
存储 编译器 C#
救命!揭秘C关键字,小白也能变大神
救命!揭秘C关键字,小白也能变大神
44 3
|
设计模式 JSON 监控
趣谈装饰器模式,让你一辈子不会忘
来看这样一个场景,上班族大多有睡懒觉的习惯,每天早上上班都时间很紧张,于是很多人为了多睡一会儿,就用更方便的方式解决早餐问题,有些人早餐可能会吃煎饼。煎饼中可以加鸡蛋,也可以加香肠,但是不管怎么加码,都还是一个煎饼。再比如,给蛋糕加上一些水果,给房子装修,都是装饰器模式。
136 0
|
前端开发 JavaScript
用Transition组件犯迷糊?看我这篇给你安排的明明白白的
transition组件作为Vue的内置组件,可以用来实现组件的过渡效果。在Vue中,过渡效果是通过CSS来实现的,所以过渡不是如何使用组件,而是如何写CSS。
145 0
用Transition组件犯迷糊?看我这篇给你安排的明明白白的
|
IDE 开发工具 Python
这样的奇技淫巧,劝你不用也罢
这样的奇技淫巧,劝你不用也罢
148 0
|
程序员 Android开发 iOS开发
程序员五一修图小贴士
程序员五一修图小贴士
160 0
程序员五一修图小贴士
|
前端开发 JavaScript
#yyds干货盘点# 前端歌谣的刷题之路-第九十八题-getter
#yyds干货盘点# 前端歌谣的刷题之路-第九十八题-getter
105 0
#yyds干货盘点# 前端歌谣的刷题之路-第九十八题-getter
|
安全 Java 编译器
学妹不懂Java泛型,非让我写一篇给她看看(有图为证)
笔者有个学妹就遇到了相同的境遇,学弟被泛型搞得头晕目眩,搞不懂泛型是个啥玩意。天天用的泛型也不知道啥玩意(她可能都不知道她有没有用泛型)。立图为证!当然,笔者深度还欠缺,如果错误还请指正!
143 0
学妹不懂Java泛型,非让我写一篇给她看看(有图为证)
|
存储 编译器
懂了嘎嘎乱杀,但我赌你会懵——指针进阶终极版
正片开始👀 细化指针这一部分内容,现在着重把一些指针的运用情景搬出来康康,如果对指针盘的非常熟练了,或者指针还出于入门阶段的铁子请绕道(晕头警告) 直接给大家盘个套餐: 一维数组👏
懂了嘎嘎乱杀,但我赌你会懵——指针进阶终极版
|
SQL
开胃菜12
开胃菜12
111 0
开胃菜12
|
JSON Java 编译器
不吹牛逼,撸个注解有什么难的
不吹牛逼,撸个注解有什么难的
141 0