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

时间:2022-07-22
本文章向大家介绍学了这个,三歪再也不想写各种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,也果不其然:

做个小调查:

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

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