「 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设计模式之建造者模式

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

相关文章
|
1天前
|
前端开发 JavaScript Java
基于Java+Springboot+Vue开发的网上商城管理系统
基于Java+Springboot+Vue开发的网上商城管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的网上商城管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。
9 2
基于Java+Springboot+Vue开发的网上商城管理系统
|
2天前
|
前端开发 JavaScript Java
基于Java+Springboot+Vue开发的民宿预订管理系统
基于Java+Springboot+Vue开发的民宿预订管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的民宿预订管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。
24 2
基于Java+Springboot+Vue开发的民宿预订管理系统
|
2天前
|
前端开发 JavaScript Java
基于Java+Springboot+Vue开发的电影订票管理系统
基于Java+Springboot+Vue开发的电影订票管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的电影订票管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。
13 1
基于Java+Springboot+Vue开发的电影订票管理系统
|
3天前
|
设计模式 Java
Java设计模式:组合模式的介绍及代码演示
组合模式是一种结构型设计模式,用于将多个对象组织成树形结构,并统一处理所有对象。例如,统计公司总人数时,可先统计各部门人数再求和。该模式包括一个通用接口、表示节点的类及其实现类。通过树形结构和节点的通用方法,组合模式使程序更易扩展和维护。
Java设计模式:组合模式的介绍及代码演示
|
3天前
|
Java 程序员 API
Java中的Lambda表达式:简化代码的秘密武器
在Java 8中引入的Lambda表达式是一种强大的编程工具,它可以显著简化代码,提高可读性。本文将介绍Lambda表达式的基本概念、优势以及在实际开发中的应用。通过具体示例,您将了解如何使用Lambda表达式来简化集合操作、线程编程和函数式编程。让我们一起探索这一革命性的特性,看看它是如何改变Java编程方式的。
17 4
|
3天前
|
Java 开发者
探索Java中的Lambda表达式:简化你的代码
【8月更文挑战第49天】在Java 8的发布中,Lambda表达式无疑是最令人兴奋的新特性之一。它不仅为Java开发者提供了一种更加简洁、灵活的编程方式,而且还极大地提高了代码的可读性和开发效率。本文将通过实际代码示例,展示如何利用Lambda表达式优化和重构Java代码,让你的编程之旅更加轻松愉快。
|
1天前
|
前端开发 JavaScript Java
基于Java+Springboot+Vue开发的鲜牛奶订购管理系统
基于Java+Springboot+Vue开发的鲜牛奶订购管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的鲜牛奶订购管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。
10 1
|
1天前
|
Java Android开发 C++
🚀Android NDK开发实战!Java与C++混合编程,打造极致性能体验!📊
在Android应用开发中,追求卓越性能是不变的主题。本文介绍如何利用Android NDK(Native Development Kit)结合Java与C++进行混合编程,提升应用性能。从环境搭建到JNI接口设计,再到实战示例,全面展示NDK的优势与应用技巧,助你打造高性能应用。通过具体案例,如计算斐波那契数列,详细讲解Java与C++的协作流程,帮助开发者掌握NDK开发精髓,实现高效计算与硬件交互。
7 1
|
2天前
|
机器学习/深度学习 数据采集 JavaScript
ADR智能监测系统源码,系统采用Java开发,基于SpringBoot框架,前端使用Vue,可自动预警药品不良反应
ADR药品不良反应监测系统是一款智能化工具,用于监测和分析药品不良反应。该系统通过收集和分析病历、处方及实验室数据,快速识别潜在不良反应,提升用药安全性。系统采用Java开发,基于SpringBoot框架,前端使用Vue,具备数据采集、清洗、分析等功能模块,并能生成监测报告辅助医务人员决策。通过集成多种数据源并运用机器学习算法,系统可自动预警药品不良反应,有效减少药害事故,保障公众健康。
ADR智能监测系统源码,系统采用Java开发,基于SpringBoot框架,前端使用Vue,可自动预警药品不良反应
|
7天前
|
存储 缓存 安全
【Java面试题汇总】多线程、JUC、锁篇(2023版)
线程和进程的区别、CAS的ABA问题、AQS、哪些地方使用了CAS、怎么保证线程安全、线程同步方式、synchronized的用法及原理、Lock、volatile、线程的六个状态、ThreadLocal、线程通信方式、创建方式、两种创建线程池的方法、线程池设置合适的线程数、线程安全的集合?ConcurrentHashMap、JUC
【Java面试题汇总】多线程、JUC、锁篇(2023版)