「 Java基础-链式调用 」Java开发中如何让你的代码看起来更优雅?试试链式调用?

简介: 我们日常在写业务代码的时候,经常会遇到一种场景,比如一个对象有很多属性,比如用户对象有很多属性:用户名、用户ID、用户性别、用户居住地址、用户工作类型、用户联系方式等等,当我们要构建一个用户对象的时候,就要不断的去`set,get`

一、前言

我们日常在写业务代码的时候,经常会遇到一种场景,比如一个对象有很多属性,比如用户对象有很多属性:用户名、用户ID、用户性别、用户居住地址、用户工作类型、用户联系方式等等,当我们要构建一个用户对象的时候,就要不断的去set,get如下代码所示:

publicclassUser {
privateStringuserName;
privateLonguserId;
privateStringuserSex;
privateStringuserAddress;
privateStringuserJob;
privateStringuserPhone;
privateStringuserBornDate;
}

这种繁琐地set值的代码,会让我们的程序看起来特别臃肿,可读性变差,为了解决这一问题,我们常用的方法一种是创建带参数的构造函数,一种是找个别的类做转换。但是,创建带参数的构造函数时,如果遇到参数太多,这个函数很长看起来很不友好的情况,而且会遇到我有时候创建需要5个,有时候需要2个参数,那就要求实体类要有多个不同参数的构造函数,要不然就在赋予参数的值的时候,直接就按最长的来,大不了用不到的位置set个null值,但是总之还是很不灵活。

二、建造者模式(Builder Pattern)

解决上述问题,我们采用一种比较优雅的方式->链式调用:chained invocation(链式调用)或者Method chaining,这种风格的API设计叫做fluent API或者Fluent interface,常用于Builder Pattern(建造者模式)。链式调用的本质就是在方法里面返回对象/其他来实现连续的调用。

2.1 什么是建造者模式?

建造者模式是一种创建型设计模式, 使你能够分步骤创建复杂对象。 该模式允许你使用相同的创建代码生成不同类型和形式的对象。

2.2 建造者模式基本介绍

2.2.1 建造者模式(Builder Pattern) 又叫生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。2.2.2 建造者模式 是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。

2.3 建造者模式适合应用场景

2.3.1 使用建造者模式可避免 “重叠构造函数 (telescoping constructor)” 的出现。2.3.2 当你希望使用代码创建不同形式的产品 (例如石头或木头房屋) 时, 可使用建造者模式。2.3.3 使用建造者构造组合树或其他复杂对象。

2.4 建造者模式优缺点

2.4.1 优点

1.可以分步创建对象, 暂缓创建步骤或递归运行创建步骤。2.生成不同形式的产品时, 可以复用相同的制造代码。3.单一职责原则。 可以将复杂构造代码从产品的业务逻辑中分离出来。

2.4.2 缺点

由于该模式需要新增多个类,因此代码整体复杂程度会有所增加。

三、链式调用在java源码中的应用

Java中,最常见的链式调用就是StringBuffer、StringBuilder 类中的 append() 方法。如下所示是StringBuilder类的源代码,篇幅所限,提取了部分代码做示例,实际开发中,我们可以通过连续的.append().append()方法来完成字符串的拼接。如下代码所示:StringBuffer、StringBuilder 这两个类都继承自抽象类 AbstractStringBuilder,该抽象类中也有append() 方法。

publicfinalclassStringBuilderextendsAbstractStringBuilderimplementsjava.io.Serializable, CharSequence{
// ... 省略代码 .../*** @throws IndexOutOfBoundsException {@inheritDoc}*/@OverridepublicStringBuilderappend(CharSequences, intstart, intend) {
super.append(s, start, end);
returnthis;
    }
@OverridepublicStringBuilderappend(char[] str) {
super.append(str);
returnthis;
    }
/*** @throws IndexOutOfBoundsException {@inheritDoc}*/@OverridepublicStringBuilderappend(char[] str, intoffset, intlen) {
super.append(str, offset, len);
returnthis;
    }
@OverridepublicStringBuilderappend(booleanb) {
super.append(b);
returnthis;
    }
// ... 省略代码 ...}

四、实现方式

4.1 通过内部类构建

@DatapublicclassUser1 {
// 真正的属性都是不可变的privatefinalintid;
privatefinalStringname;
privatefinalStringjob;
privatefinalStringaddress;
privatefinalDatebirthday;
// 私有构造方法,只被 Builder 类调用privateUser1(Builderbuilder) {
this.id=builder.id;
this.name=builder.name;
this.job=builder.job;
this.address=builder.address;
this.birthday=builder.birthday;
    }
publicstaticclassBuilder {
// 必须参数privateintid;
privateStringname;
privateDatebirthday;
// 可选参数privateStringjob;
privateStringaddress;
publicBuilder(intid, Stringname, Datebirthday) {
this.id=id;
this.name=name;
this.birthday=birthday;
        }
//使用设置好的参数值新建 OperateLog 对象publicUser1build(){
returnnewUser1(this);
        }
// 每个 setter 方法都返回当前的对象,做到链式调用publicBuildersetJob(Stringjob) {
this.job=job;
returnthis;
        }
publicBuildersetAddress(Stringaddress) {
this.address=address;
returnthis;
        }
    }
}

对象内部类的bulider大概分成四部分:

1、 一个简单的内部类,里面的属性和User属性相同;2、 内部类的构造函数;3、 bulid方法,真正核心的一个方法,直接返回一个User实例;4、 属性的set方法,这一部分都是平行的方法;

客户端类调用实例:

//建造者模式只有在调用build()之后才会创建OperateLog对象。User1user1=newUser1.Builder(1,"小明",newDate()).setJob("软件工程师").setAddress("北京").build();

4.2 使用lombok@Builder注解

@Data@AllArgsConstructor@NoArgsConstructor@BuilderpublicclassUser2 {
privateStringname;
privateStringjob;
}

4.3 使用lombok@RequiredArgsConstructor@NonNull注解

@Data@Accessors(chain=true)
@RequiredArgsConstructor(staticName="of")
publicclassUser3 {
@NonNullprivateStringname;
privateStringjob;
}

客户端类分别采用上述3种方式构建对象:

publicclassClient {
publicstaticvoidmain(String[] args) {
// 第一种 建造者模式只有在调用build()之后才会创建User1对象User1user1=newUser1.Builder(1,"小明",newDate()).setJob("软件工程师").setAddress("北京").build();
System.out.println(user1);
// 第二种User2user2=User2.builder().name("小明").job("软件工程师").build();
System.out.println(user2);
// 第三种User3user3=User3.of("小明").setJob("软件工程师");
System.out.println(user3);
    }
}

控制台输出:

User1(id=1, name=小明, job=软件工程师, address=北京, birthday=SunFeb1921:11:12CST2023)
User2(name=小明, job=软件工程师)
User3(name=小明, job=软件工程师)

五、什么情况下适合采用这种链式的方法调用?

上述代码演示的链式调用,实际上是同一个对象的多个方法的连续调用。也就是说,在这个长链中的每个方法返回的都是相同的类型、相同的对象,即当前对象本身。例如,StringBuilderappend方法的连续调用,JSONObject中的accumulateput等方法也可以连续调用。这些被调用的方法都有“构建”的特性,都是用于完善实例对象。使用链式调用代码容易编写,看起来比较简洁也容易阅读和理解。如果被调用的方法返回的类型不同,则不适合链式调用。因为各方法返回的类型被隐藏了,代码不容易理解,另外在调试的时候也是比较麻烦的。

六、总结

6.1 优点

编程性强 、可读性强、代码简洁。

6.2 缺点

不太利于代码调试

七、参考 & 鸣谢

1、学习笔记Java链式调用(方法链)

2、【Java】子类的链式调用

3、java设计模式之建造者模式

感谢前人的经验、分享和付出,让我们可以有机会站在巨人的肩膀上眺望星辰大海!

相关文章
|
8天前
|
Java
在 Java 中捕获和处理自定义异常的代码示例
本文提供了一个 Java 代码示例,展示了如何捕获和处理自定义异常。通过创建自定义异常类并使用 try-catch 语句,可以更灵活地处理程序中的错误情况。
|
7天前
|
Java API Maven
如何使用Java开发抖音API接口?
在数字化时代,社交媒体平台如抖音成为生活的重要部分。本文详细介绍了如何用Java开发抖音API接口,从创建开发者账号、申请API权限、准备开发环境,到编写代码、测试运行及注意事项,全面覆盖了整个开发流程。
48 10
|
13天前
|
SQL 安全 Java
安全问题已经成为软件开发中不可忽视的重要议题。对于使用Java语言开发的应用程序来说,安全性更是至关重要
在当今网络环境下,Java应用的安全性至关重要。本文深入探讨了Java安全编程的最佳实践,包括代码审查、输入验证、输出编码、访问控制和加密技术等,帮助开发者构建安全可靠的应用。通过掌握相关技术和工具,开发者可以有效防范安全威胁,确保应用的安全性。
28 4
|
15天前
|
缓存 监控 Java
如何运用JAVA开发API接口?
本文详细介绍了如何使用Java开发API接口,涵盖创建、实现、测试和部署接口的关键步骤。同时,讨论了接口的安全性设计和设计原则,帮助开发者构建高效、安全、易于维护的API接口。
42 4
|
15天前
|
Java 大数据 API
14天Java基础学习——第1天:Java入门和环境搭建
本文介绍了Java的基础知识,包括Java的简介、历史和应用领域。详细讲解了如何安装JDK并配置环境变量,以及如何使用IntelliJ IDEA创建和运行Java项目。通过示例代码“HelloWorld.java”,展示了从编写到运行的全过程。适合初学者快速入门Java编程。
|
19天前
|
jenkins Java 测试技术
如何使用 Jenkins 自动发布 Java 代码,通过一个电商公司后端服务的实际案例详细说明
本文介绍了如何使用 Jenkins 自动发布 Java 代码,通过一个电商公司后端服务的实际案例,详细说明了从 Jenkins 安装配置到自动构建、测试和部署的全流程。文中还提供了一个 Jenkinsfile 示例,并分享了实践经验,强调了版本控制、自动化测试等关键点的重要性。
53 3
|
18天前
|
Java
Java代码解释++i和i++的五个主要区别
本文介绍了前缀递增(++i)和后缀递增(i++)的区别。两者在独立语句中无差异,但在赋值表达式中,i++ 返回原值,++i 返回新值;在复杂表达式中计算顺序不同;在循环中虽结果相同但使用方式有别。最后通过 `Counter` 类模拟了两者的内部实现原理。
Java代码解释++i和i++的五个主要区别
|
14天前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
28 0
WK
|
20天前
|
开发框架 移动开发 Java
C++和Java哪个更适合开发移动应用
本文对比了C++和Java在移动应用开发中的优劣,从市场需求、学习难度、开发效率、跨平台性和应用领域等方面进行了详细分析。Java在Android开发中占据优势,而C++则适合对性能要求较高的场景。选择应根据具体需求和个人偏好综合考虑。
WK
38 0
|
6月前
|
Java
使用Java代码打印log日志
使用Java代码打印log日志
316 1
下一篇
无影云桌面