从 Java Builder Pattern 到 return this 链式调用

起因

第一次接触到 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 和链式调用的一条线和一些感悟吧!

全文写下来,自我感觉还是相当清晰的,同时,写完这篇文章后,我自己也做了一遍回顾,收获很大!同时,细节处我没有多说,但是都有链接,有兴趣的朋友可以看看。

最后,本文纯属个人之见,如有错误之处,还望指正,不胜感激!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

十甫寸木南

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值