开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 11 天,点击查看活动详情
我们日常在写业务代码的时候,经常会遇到一种场景,比如一个对象有很多属性,比如用户对象有很多属性:用户名、用户ID、用户性别、用户居住地址、用户工作类型、用户联系方式等等,当我们要构建一个用户对象的时候,就要不断的去set,get
如下代码所示:
public class User {
private String userName;
private Long userId;
private String userSex;
private String userAddress;
private String userJob;
private String userPhone;
private String userBornDate;
}
复制代码
这种繁琐
地set值的代码,会让我们的程序看起来特别臃肿,可读性变差,为了解决这一问题,我们常用的方法一种是创建带参数的构造函数,一种是找个别的类做转换。但是,创建带参数的构造函数时,如果遇到参数太多,这个函数很长看起来很不友好的情况,而且会遇到我有时候创建需要5个,有时候需要2个参数,那就要求实体类要有多个不同参数的构造函数,要不然就在赋予参数的值的时候,直接就按最长的来,大不了用不到的位置set个null值,但是总之还是很不灵活。
解决上述问题,我们采用一种比较优雅的方式->链式调用:chained invocation(链式调用)
或者Method chaining
,这种风格的API设计叫做fluent API
或者Fluent interface
,常用于Builder Pattern(建造者模式)
。链式调用的本质就是在方法里面返回对象/其他来实现连续的调用。
建造者模式是一种创建型设计模式
, 使你能够分步骤创建复杂对象。 该模式允许你使用相同的创建代码生成不同类型和形式的对象。
2.2.1 建造者模式(Builder Pattern
) 又叫生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。
2.2.2 建造者模式 是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。
2.3.1 使用建造者模式可避免 “重叠构造函数 (telescoping constructor
)” 的出现。
2.3.2 当你希望使用代码创建不同形式的产品 (例如石头或木头房屋) 时, 可使用建造者模式。
2.3.3 使用建造者构造组合树或其他复杂对象。
2.4.1 优点
1.可以分步创建对象, 暂缓创建步骤或递归运行创建步骤。2.生成不同形式的产品时, 可以复用相同的制造代码。
3.单一职责原则。 可以将复杂构造代码从产品的业务逻辑中分离出来。
2.4.2 缺点
由于该模式需要新增多个类,因此代码整体复杂程度会有所增加。
Java中,最常见的链式调用就是StringBuffer、StringBuilder
类中的 append()
方法。如下所示是StringBuilder
类的源代码,篇幅所限,提取了部分代码做示例,实际开发中,我们可以通过连续的.append().append()
方法来完成字符串的拼接。如下代码所示:StringBuffer、StringBuilder
这两个类都继承自抽象类 AbstractStringBuilder
,该抽象类中也有append()
方法。
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
// ... 省略代码 ...
/**
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
@Override
public StringBuilder append(CharSequence s, int start, int end) {
super.append(s, start, end);
return this;
}
@Override
public StringBuilder append(char[] str) {
super.append(str);
return this;
}
/**
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
@Override
public StringBuilder append(char[] str, int offset, int len) {
super.append(str, offset, len);
return this;
}
@Override
public StringBuilder append(boolean b) {
super.append(b);
return this;
}
// ... 省略代码 ...
}
复制代码
@Data
public class User1 {
// 真正的属性都是不可变的
private final int id;
private final String name;
private final String job;
private final String address;
private final Date birthday;
// 私有构造方法,只被 Builder 类调用
private User1(Builder builder) {
this.id = builder.id;
this.name = builder.name;
this.job = builder.job;
this.address = builder.address;
this.birthday = builder.birthday;
}
public static class Builder {
// 必须参数
private int id;
private String name;
private Date birthday;
// 可选参数
private String job;
private String address;
public Builder(int id, String name, Date birthday) {
this.id = id;
this.name = name;
this.birthday = birthday;
}
//使用设置好的参数值新建 OperateLog 对象
public User1 build(){
return new User1(this);
}
// 每个 setter 方法都返回当前的对象,做到链式调用
public Builder setJob(String job) {
this.job = job;
return this;
}
public Builder setAddress(String address) {
this.address = address;
return this;
}
}
}
复制代码
对象内部类的bulider
大概分成四部分:
1、 一个简单的内部类,里面的属性和User
属性相同;2、 内部类的构造函数;
3、
bulid
方法,真正核心的一个方法,直接返回一个User
实例;4、 属性的
set
方法,这一部分都是平行的方法;
客户端类调用实例:
//建造者模式只有在调用build()之后才会创建OperateLog对象。
User1 user1 = new User1.Builder(1,"小明",new Date()).setJob("软件工程师").setAddress("北京").build();
复制代码
lombok
的@Builder
注解@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class User2 {
private String name;
private String job;
}
复制代码
lombok
的@RequiredArgsConstructor
和@NonNull
注解@Data
@Accessors(chain = true)
@RequiredArgsConstructor(staticName = "of")
public class User3 {
@NonNull
private String name;
private String job;
}
复制代码
客户端类分别采用上述3种方式构建对象:
public class Client {
public static void main(String[] args) {
// 第一种 建造者模式只有在调用build()之后才会创建User1对象
User1 user1 = new User1.Builder(1,"小明",new Date()).setJob("软件工程师").setAddress("北京").build();
System.out.println(user1);
// 第二种
User2 user2 = User2.builder().name("小明").job("软件工程师").build();
System.out.println(user2);
// 第三种
User3 user3 = User3.of("小明").setJob("软件工程师");
System.out.println(user3);
}
}
复制代码
控制台输出:
User1(id=1, name=小明, job=软件工程师, address=北京, birthday=Sun Feb 19 21:11:12 CST 2023)
User2(name=小明, job=软件工程师)
User3(name=小明, job=软件工程师)
复制代码
上述代码演示的链式调用,实际上是同一个对象的多个方法的连续调用。也就是说,在这个长链中的每个方法返回的都是相同的类型、相同的对象,即当前对象本身。例如,StringBuilder
中append
方法的连续调用,JSONObject
中的accumulate
、put
等方法也可以连续调用。这些被调用的方法都有“构建”的特性,都是用于完善实例对象。使用链式调用代码容易编写,看起来比较简洁也容易阅读和理解。如果被调用的方法返回的类型不同,则不适合链式调用。
因为各方法返回的类型被隐藏了,代码不容易理解,另外在调试的时候也是比较麻烦的。
编程性强 、可读性强、代码简洁。
不太利于代码调试
1、学习笔记Java链式调用(方法链)
感谢前人的经验、分享和付出,让我们可以有机会站在巨人的肩膀上眺望星辰大海!
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 11 天,点击查看活动详情
原网址: 访问
创建于: 2023-06-12 10:46:13
目录: default
标签: 无
未标明原创文章均为采集,版权归作者所有,转载无需和我联系,请注明原出处,南摩阿彌陀佛,知识,不只知道,要得到
最新评论