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建造者模式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring中TransactionSynchronizationManager的使用详解

    Spring中TransactionSynchronizationManager的使用详解

    这篇文章主要介绍了Spring中TransactionSynchronizationManager的使用详解,TransactionSynchronizationManager是事务同步管理器,监听事务的操作,来实现在事务前后可以添加一些指定操作,需要的朋友可以参考下
    2023-09-09
  • java实现将结果集封装到List中的方法

    java实现将结果集封装到List中的方法

    这篇文章主要介绍了java实现将结果集封装到List中的方法,涉及java数据库查询及结果集转换的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2016-07-07
  • 上传自己的jar包到maven中央仓库的快速操作方法

    上传自己的jar包到maven中央仓库的快速操作方法

    网络上可以搜索到很多jar包到中央仓库,但是都不是多适合自己的项目,于是自己动手写个,本文档通过sonatype上传jar包至maven中央仓库,Sonatype通过JIRA来管理OSSRH仓库,具体实例代码跟随小编一起看看吧
    2021-08-08
  • Java基于Socket的文件传输实现方法

    Java基于Socket的文件传输实现方法

    这篇文章主要介绍了Java基于Socket的文件传输实现方法,结合实例分析了Java使用Socket实现文件传输的建立连接、发送与接收消息、文件传输等相关技巧,需要的朋友可以参考下
    2015-12-12
  • Spring Boot实现邮件服务(附:常见邮箱的配置)

    Spring Boot实现邮件服务(附:常见邮箱的配置)

    这篇文章主要给大家介绍了关于Spring Boot实现邮件服务的相关资料,文中还附上了常见邮箱的配置,通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-12-12
  • java时间相关处理小结

    java时间相关处理小结

    这篇文章介绍了java时间相关处理,有需要的朋友可以参考一下
    2013-11-11
  • 浅谈一下Java中的ReentrantLock

    浅谈一下Java中的ReentrantLock

    这篇文章主要介绍了浅谈一下Java中的ReentrantLock,这个类是JUC工具包中对线程安全问题提供的一种解决方案,它主要是用来给对象上锁,保证同一时间这能有一个线程在访问当前对象,需要的朋友可以参考下
    2023-09-09
  • java判断今天,昨天,前天,不能用秒间隔的简单实例

    java判断今天,昨天,前天,不能用秒间隔的简单实例

    下面小编就为大家带来一篇java判断今天,昨天,前天,不能用秒间隔的简单实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-03-03
  • Spring Boot 基于注解的 Redis 缓存使用详解

    Spring Boot 基于注解的 Redis 缓存使用详解

    本篇文章主要介绍了Spring Boot 基于注解的 Redis 缓存使用详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-05-05
  • 几种JAVA细粒度锁的实现方式

    几种JAVA细粒度锁的实现方式

    这篇文章主要为大家详细介绍了几种JAVA细粒度锁的实现方式,感兴趣的小伙伴们可以参考一下
    2016-05-05

最新评论