Java设计模式中的建造者(Builder)模式解读

 更新时间:2023年10月09日 09:58:47   作者:蜡笔小久  
这篇文章主要介绍了Java设计模式中的建造者(Builder)模式解读, 建造者模式是一种创建对象的设计模式,它通过将对象的构建过程分解为多个步骤,并使用一个建造者类来封装这些步骤,从而使得对象的构建过程更加灵活和可扩展,需要的朋友可以参考下

为什么需要建造者模式

在我们日常的开发中,一般使用new 关键字通过构造器就可以实现对象的创建,然后通过set来实现成员变量的修改, 为什么在Java中还需要建造者模式来创建对象?

假如我们系统中,需要有一个统一的日志模块,包含了日志内容(content)、日志所属组织(orgId)、日志来源(logSource: 如app, 平台操作)、用户名(username)。

通常情况下我们可以直接通过定义一个日志类, 然后通过构造器的方式, 进行参数设置。

public class LogEntity {
    @Getter
    private Long id;
    /**
     * 操作时间
     */
    @Getter
    private String eventDate;
    /**
     * 操作日志模块: default other
     */
    @Getter
    private String module;
    /**
     * 操作内容
     */
    @Getter
    private String message;
    /**
     * IP
     */
    @Getter
    private String ipAddress;
    /**
     * 操作用户
     */
    @Getter
    private String username;
    /**
     * 日志所属组织
     */
    @Getter
    private Long organizationId;
    /**
     * 日志来源('日志来源:1:终端上报,2:平台下发,3:平台操作,4:APP操作')
     */
    @Getter
    private Integer logSource;
    public PlatformLoggerEntity(Long id, String module, String message, Long organizationId, Integer logSource) {
        this.id = id;
        this.module = module;
        this.message = message;
        this.organizationId = organizationId;
        this.logSource = logSource;
    }
}

 现在日志类,只有5个参数,参数的个数还在可接受范围内,假如后续LogEntity类要进行扩展, 存储的参数变成10个或则更多, 构造器就会变得很长,并且可读性和易用性都很变得很差。在使用构造器时,还需要记得每个位置传递的是什么字段,如果字段类型相同,还有可能出现传递错误的情况。 

你可能会说,我们可以通过set方法来设置值,确实,通过set方法来设置值可以避免必填字段和非必填字段问题。 代码的可用性也提高了。

出现的问题

1.id作为必填字段,需要写在构造器中,如果必填字段过多,又会出现构造器参数列表过长的问题

2. 字段间存在关联关系,比如logSource是平台,则需要设置用户的所属企业id和用户名,  如果我们通过set来设置值,那我们校验的逻辑就没有地方放了

3. 如果我们想把LogEntity设置为不可变对象,对象初始化后就不允许改变,那么我们再暴露set方法,就达到到该效果

因此我们可以通过建造者模式重写方面的类, 重写好的类如下:

public class PlatformLoggerEntity {
    /**
     * 使用雪花算法生成ID
     */
    @Getter
    private Long id;
    /**
     * 操作时间
     */
    @Getter
    private String eventDate;
    /**
     * 操作日志模块: default other
     */
    @Getter
    private String module;
    /**
     * 操作内容
     */
    @Getter
    private String message;
    /**
     * IP
     */
    @Getter
    private String ipAddress;
    /**
     * 操作用户
     */
    @Getter
    private String username;
    /**
     * 日志所属组织
     */
    @Getter
    private Long organizationId;
    /**
     * 日志来源('日志来源:1:终端上报,2:平台下发,3:平台操作,4:APP操作')
     */
    @Getter
    private Integer logSource;
    /**
     * 显示类型 0:正常显示  1:超链显示
     */
    @Getter
    private Integer showType;
    private PlatformLoggerEntity(Builder builder) {
        this.id = IdUtil.next();
        this.username = SystemHelper.getCurrentUserName();
        this.ipAddress = SystemHelper.getIpAddress();
        this.organizationId = builder.organizationId;
        this.module = builder.module.getModule();
        this.message = builder.message;
        this.monitoringOperation = builder.monitoringOperation;
        this.monitorName = builder.monitorName;
        this.plateColor = builder.plateColor;
        this.showType = builder.showType;
        this.eventDate = LocalDateUtils.dateTimeFormat(new Date());
        this.logSource = builder.logSource.getLoggerSource();
    }
    /**
     * @return 创建builder类的实例对象
     */
    public static Builder newBuilder() {
        return new Builder();
    }
    /**
     * 日誌Builder类
     */
    public static class Builder {
        /**
         * 操作日志模块: default other
         */
        private LoggerModule module;
        /**
         * 操作内容
         */
        private String message;
        /**
         * 日志所属组织
         */
        private Long organizationId;
        /**
         * 日志来源('日志来源:1:终端上报,2:平台下发,3:平台操作,4:APP操作')
         */
        private LoggerSource logSource;
        /**
         * 显示类型 0:正常显示  1:超链显示
         */
        private Integer showType;
        publick LogEntity build() {
            // 默认为平台操作
            if (Objects.isNull(logSource)) {
                this.logSource = LoggerSource.PLATFORM_OPERATION;
            }
            if (Objects.isNull(organizationId)) {
                this.organizationId = SystemHelper.getCurrentUserOrgId();
            }
            // 用于前端展示
            if (Objects.isNull(module)) {
                this.module = LoggerModule.DEFAULT;
                this.showType = LoggerConstant.SHOW_TYPE_NORMAL;
            } else if (module.equals(LoggerModule.DEFAULT)) {
                this.showType = LoggerConstant.SHOW_TYPE_NORMAL;
            } else {
                this.showType = LoggerConstant.SHOW_TYPE_HYPERLINK;
            }
            return new PlatformLoggerEntity(this);
        }
        public Builder module(LoggerModule module) {
            this.module = module;
            return this;
        }
        public Builder message(String message) {
            this.message = message;
            return this;
        }
        public Builder organizationId(Long organizationId) {
            this.organizationId = organizationId;
            return this;
        }
        public Builder logSource(LoggerSource logSource) {
            this.logSource = logSource;
            return this;
        }
    }
}

我们重写LogEntity类后, 我们可以通过newBuilder来获取内部的Builder对象,并且通过build函数来创建LogEntity对象。 

1.从上面代码可以看出LogEntitty所有的成员属性都只有get方法,避免出现随意修改的情况

2.通过LogEntity内部的Builder类中的build() 来增加了相关联属性的验证,避免调用则漏传或则错传参数

总结

什么情况下我们可以选择建造者(Builder)模式来创建对象, 总结了一下三点:

1.如果类中的必填字段过多,构造函数过长,生成对象时需要校验必填属性

2.如果字段之间存在关联,生成对象时进行校验。

3.我们需要创建不可变对象,不能暴露实体的set方法时

到此这篇关于Java设计模式中的建造者(Builder)模式解读的文章就介绍到这了,更多相关Java建造者模式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringMVC中处理Http请求的原理详解

    SpringMVC中处理Http请求的原理详解

    这篇文章主要介绍了SpringMVC中处理Http请求的原理详解,当一个http请求过来了首先经过的是DispatcherServlet这么一个前端控制器并调用了这个前端控制器的doService方法,这个方法最终我们发现它调用了doDispatcher这么一个方法,需要的朋友可以参考下
    2023-12-12
  • Java中static和static final的区别详解

    Java中static和static final的区别详解

    这篇文章主要介绍了Java中static和static final的区别详解,开发时我们经常用到static以及static final来修饰我们的字段变量,那么他们到底有什么区别呢?其实他们的区别可以用使用字节码文件来解析,需要的朋友可以参考下
    2023-10-10
  • java实现窗口刷新的示例代码

    java实现窗口刷新的示例代码

    本文主要介绍了java实现窗口刷新的示例代码,通过重写paintComponent()方法和调用repaint()方法,可以实现窗口的即时刷新,具有一定的参考价值,感兴趣的可以了解一下
    2024-02-02
  • 浅析Java如何优雅的设计接口状态码和异常

    浅析Java如何优雅的设计接口状态码和异常

    HTTP协议里定义了一系列的状态码用来表明请求的状态,如常用的200表示请求正常,404表示请求的资源不存在,所以本文就来和大家讨论一下如何优雅的设计接口状态码和异常,感兴趣的可以了解下
    2024-03-03
  • java hasNext()使用实例解析

    java hasNext()使用实例解析

    这篇文章主要介绍了java hasNext()使用实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-09-09
  • Java 线程池_动力节点Java学院整理

    Java 线程池_动力节点Java学院整理

    系统启动一个新线程的成本是比较高的,因为它涉及到与操作系统的交互。在这种情况下,使用线程池可以很好的提供性能,尤其是当程序中需要创建大量生存期很短暂的线程时,更应该考虑使用线程池
    2017-05-05
  • SpringBoot中定时任务@Scheduled的多线程使用详解

    SpringBoot中定时任务@Scheduled的多线程使用详解

    这篇文章主要为大家详细介绍了pring Boot定时任务@Scheduled的多线程原理以及如何加入线程池来处理定时任务,感兴趣的可以了解一下
    2023-04-04
  • Spring MVC+mybatis实现注册登录功能

    Spring MVC+mybatis实现注册登录功能

    这篇文章主要为大家详细介绍了Spring MVC+mybatis实现注册登录功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-07-07
  • 详解Java中日期工具类的操作

    详解Java中日期工具类的操作

    这篇文章主要为大家详细介绍了Java中日期工具类的常见操作,如:字符串和Date互转、字符串和LocalDate互转等,感兴趣的小伙伴可以学习一下
    2022-11-11
  • SpringCloud中使用webclient(get和post)请求微服务接口数据

    SpringCloud中使用webclient(get和post)请求微服务接口数据

    在SpringCloud项目中使用WebClient调用微服务时,涉及配置WebClient、发起get和post请求等操作,如请求头设置、服务地址配置、数据转换处理、异常处理等,避免在循环中使用WebClient请求、路径设置细节以及数据返回处理技巧,本文旨在帮助理解和应用WebClient进行微服务调用
    2024-10-10

最新评论