目录
起因
第一次接触到 Java Builder Pattern
是在刚进公司时的培训中,当时做一个仿真项目,是用 Java 做的,而其中使用了 Lombok 。
也是因此,当时感觉 @Builder
注解简直太好用了,写出来的代码也非常优雅,比如下面这样:⬇️
People people = People.builder()
.name("Jack")
.age(18)
.address("Xi'an")
.gender("Male")
.build();
非常完美地规避了构造函数参数过多的情况,而且所有的属性都是可选的,也因此,当时非常想深入了解一下 Java Builder Pattern
的原理,但是,由于拖延症(哈哈),一直也没有去了解过。
再一次动这个念头,是在上了项目后,由于使用的语言是 TypeScript
,而在看文档的时候,突然看到了一个标题,叫做 Polymorphic this types
,也就是说 TypeScript 允许指定一个方法的返回值类型为 this
,而 this
具体所指代的类型,会根据当前对象的具体 Class 决定。
当时看到这个,第一感觉就是:好神奇!
而第二感觉就是:这个似乎和 Java Builder Pattern
有点类似啊!
于是,借着这个机会,我去查了一下,然后才明白了 Java Builder Pattern
的原理。但是,知道了原理后,也并没有再去深究。
第三次,或者说是第二次碰到 return this
,是在最近的公司内部培训过程中,由于需要重构一段代码,我再次想到了 return this
的写法。
于是,趁着最近刚好有时间,决定把这个过程记录下来。
如果我的理解或者文章内容有错误之处,看到的朋友,还望指正,不胜感激!
Java Builder Pattern
由于本文只是一篇记录文章,同时 Java Builder Pattern 原理也确实没有过于复杂,所以我这里就不仔细解释其原理了,只单独列出一个例子,以作说明,想要深入了解的朋友可以参考后面的几篇文章(即便我自己解释也不会有这几篇文章解释得好的)。
例程
class People {
private String name;
private String gender;
private int age;
private String userId;
private String ip;
private String birthday;
private String nationality;
private String address;
private String phone;
// 仅做演示,不写 get 方法了,太长了
private People(Builder builder) {
this.name = builder.name;
this.gender = builder.gender;
this.age = builder.age;
this.userId = builder.userId;
this.ip = builder.ip;
this.birthday = builder.birthday;
this.nationality = builder.nationality;
this.address = builder.address;
this.phone = builder.phone;
}
public static class Builder {
private String name;
private String gender;
private int age;
private String userId;
private String ip;
private String birthday;
private String nationality;
private String address;
private String phone;
public Builder setName(String name) {
this.name = name;
return this;
}
public Builder setGender(String gender) {
this.gender = gender;
return this;
}
public Builder setAge(int age) {
this.age = age;
return this;
}
public Builder setUserId(String userId) {
this.userId = userId;
return this;
}
public Builder setIp(String ip) {
this.ip = ip;
return this;
}
public Builder setBirthday(String birthday) {
this.birthday = birthday;
return this;
}
public Builder setNationality(String nationality) {
this.nationality = nationality;
return this;
}
public Builder setAddress(String address) {
this.address = address;
return this;
}
public Builder setPhone(String phone) {
this.phone = phone;
return this;
}
public People build() {
return new People(this);
}
}
}
// 用法如下
People people = new People.Builder()
.setName("Jack")
.setAge(18)
.setIp("0.0.0.0")
.setGender("Male")
.setBirthday("1996-03-13")
.setPhone("13686258456")
.setAddress("Xi'an")
.setNationality("China")
.setUserId("000000000001")
.build();
参考文章
TypeScript return this
Polymorphic this types
针对这个 Polymorphic this types
,当时看到的时候还是很惊艳的。因为虽然之前也写过一些 Java 的代码,包括在开始接触 TypeScript 之后也尝试着为方法和变量都加上返回类型,但,也许是一个盲区吧,从来没有想过还可以直接 return this
。
这无疑是相当于发现了新大陆。
而对此,官网给出的例程也是相当得简单粗暴,直接将优点展示了出来,如下:
例程
class BasicCalculator {
public constructor(protected value: number = 0) {}
public currentValue(): number {
return this.value;
}
public add(operand: number): this {
this.value += operand;
return this;
}
public multiply(operand: number): this {
this.value *= operand;
return this;
}
// ... other operations go here ...
}
let v = new BasicCalculator(2)
.multiply(5)
.add(1)
.currentValue();
同时,在官网给出的例程中,还有一个针对以上例程的一个扩展,而那个扩展也是真正展现了 return this
的强大,也是我在用 Java 尝试时没做到的。
有兴趣的可以去官方文档看一下。
参考资料
Java return this
在写到这里的时候,我心里其实是有点遗憾的,因为这里标题处所指 return this
其实是方法体中的返回语句 return this
。比如:⬇️
public Calculator add(int operand) {
this.value += operand;
return this;
}
而在上面提到的 TypeScript 中的 return this
,其实是真正的返回类型是 this
,而并不仅仅是一个返回语句。比如:⬇️
public add(operand: number): this {
this.value += operand;
return this;
}
在这一点上,我有点遗憾,因为我本人其实是非常喜欢 Java 的,觉得 Java 天下第一!😂(觉得 php 最好的别喷我😂)不过呢,我也并没那么死板,每个语言都有自己的优势和缺点,这也没什么。而且,也有可能是我自己学识不够,不知道怎么写而已。
当然了,此处也不聚焦于此,就不多说了。
下面呢,来看一下我用 Java 的 return this
写的一个 Calculator ,如下:⬇️
例程
class Calculator {
private int total;
public Calculator(int cardinality) {
this.total = cardinality;
}
public Calculator add(int addend) {
this.total += addend;
return this;
}
public Calculator minus(int minus) {
this.total -= minus;
return this;
}
public Calculator multiply(int multiplier) {
this.total *= multiplier;
return this;
}
public Calculator divideBy(int divisor) {
this.total /= divisor;
return this;
}
public Calculator calculate() {
return this;
}
public int getTotal() {
return total;
}
}
将方法的返回值类型指定为当前 Class ,那么,在方法体的最后可以直接 return this
,从而可以形成一个类似于 Builder Patern 的效果。
用法如下:⬇️
Calculator calculator = new Calculator(5)
.add(3)
.minus(4)
.multiply(2)
.divideBy(4)
.calculate();
同时,有兴趣的可以尝试一下像 TypeScript 那样使用一个子类来继承一下上述的 Calculator 类,会发现,返回值类型确实不是很灵活。(由于并非重点,这里就不赘述了)
参考资料
链式调用
说到链式调用呢,第一次接触是在 Java Stream, 当时由于是第一次用流式处理,感觉明明一个循环搞定,为什么要搞得这么复杂呢?
但是后来,写得多了,发现是真好用(真香😂)。
然后呢,在使用 Stream 的过程中呢,也渐渐喜欢上了 Stream 的链式调用,感觉写出的代码真是优雅得不行,就像一个艺术品,包括接触到的 StringBuilder 的 append()
,也是一样,链式调用的方式有一种优雅美!
然后呢,我前面也说了,在重构一段代码的时候,想到了链式调用,那么,一起来看一下吧!
需求描述
恰逢植树节到了,某学校某班级准备组织学生们去山上植树,于是,老师让学生们挨个报数,准备统计一下人数。但是,谁也没想到的是,一个小小的植树节活动,居然让一些学生亮出了自己的“真实身份”。
if 条件判断
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
class Game {
private List<String> names;
private Game(List<String> names) {
this.names = names;
}
private String countOff() {
StringBuilder result = new StringBuilder();
AtomicInteger num = new AtomicInteger();
this.names.forEach(name -> {
if ("Rose".equals(name)) {
result.append("我请假\n");
}
if ("Li Si".equals(name)) {
result.append("我爸是李刚\n");
}
if ("Xiao Hong".equals(name)) {
result.append("我这几天不舒服\n");
}
if ("Liu Xiaoyun".equals(name)) {
result.append("我妈是副校长\n");
}
if (!Arrays.asList("Rose", "Li Si", "Xiao Hong", "Liu Xiaoyun").contains(name)) {
result.append(num.incrementAndGet()).append("\n");
}
});
return result.toString().trim();
}
public static void main(String[] args) {
List<String> names = Arrays.asList("Jack", "Tom", "Rose", "Bob", "Zhang San", "Li Si", "Xiao Hong", "Liu Xiaoyun", "Xiao Ming", "Danny", "Catherine");
Game game = new Game(names);
System.out.println(game.countOff());
}
}
// 最终报数结果
1
2
我请假
3
4
我爸是李刚
我这几天不舒服
我妈是副校长
5
6
7
链式调用
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
import static java.util.regex.Pattern.*;
class GameWithoutIf {
private List<String> names;
private GameWithoutIf(List<String> names) {
this.names = names;
}
private String countOff() {
StringBuilder result = new StringBuilder();
AtomicInteger num = new AtomicInteger(1);
this.names.forEach(name -> {
String word = CountOffChecker.of(name)
.checkAndCountOff("Rose", "我请假\n")
.checkAndCountOff("Li Si", "我爸是李刚\n")
.checkAndCountOff("Xiao Hong", "我这几天不舒服\n")
.checkAndCountOff("Liu Xiaoyun", "我妈是副校长\n")
.checkAndCountOff(num.get())
.toString();
result.append(word);
checkAndIncreate(num, word);
});
return result.toString();
}
private void checkAndIncreate(AtomicInteger num, String word) {
if (GameWithoutIf.isNumeric(word.replace("\n", ""))) {
num.incrementAndGet();
}
}
private static boolean isNumeric(String str){
Pattern pattern = compile("[0-9]*");
return pattern.matcher(str).matches();
}
public static void main(String[] args) {
List<String> names = Arrays.asList("Jack", "Tom", "Rose", "Bob", "Zhang San", "Li Si", "Xiao Hong", "Liu Xiaoyun", "Xiao Ming", "Danny", "Catherine");
GameWithoutIf game = new GameWithoutIf(names);
System.out.println(game.countOff());
}
}
class CountOffChecker {
private String name;
private StringBuilder result = new StringBuilder();
private CountOffChecker(String name) {
this.name = name;
}
CountOffChecker checkAndCountOff(String specialName, String words) {
if (this.name.equals(specialName)) {
this.result.append(words);
}
return this;
}
CountOffChecker checkAndCountOff(int num) {
if ("".equals(this.result.toString())) {
this.result.append(num).append("\n");
}
return this;
}
static CountOffChecker of(String name) {
return new CountOffChecker(name);
}
@Override
public String toString() {
return this.result.toString();
}
}
// 最终报数结果
1
2
我请假
3
4
我爸是李刚
我这几天不舒服
我妈是副校长
5
6
7
可以看到,我将原本的一堆 if 条件判断换成了一个链式的调用,看起来就清晰了很多(个人觉得)。
当然了,以上的这个例子也许并不是很恰当,同时也可能有点过度的感觉,但是个人感觉,还是清晰了很多的(方法名也有点不太恰当)。
当然,什么例子无所谓,主要是想要借此记录、阐述一下 return this
这个点,说明链式调用相比 if 条件判断的清晰之处以及其优点;再有就是,不要忘了代码还可以这么写😂
总结
其实没什么总结的,这篇文章主要就是提供一个记录,记录下我从接触 Java Builder Pattern 到 TypeScript Polymorphic this types, 再到 return this 和链式调用的一条线和一些感悟吧!
全文写下来,自我感觉还是相当清晰的,同时,写完这篇文章后,我自己也做了一遍回顾,收获很大!同时,细节处我没有多说,但是都有链接,有兴趣的朋友可以看看。
最后,本文纯属个人之见,如有错误之处,还望指正,不胜感激!