Java中如何优雅的进行含优先级的数据获取(链式调用)
Java中如何优雅的进行含优先级的数据获取(链式调用)
在实际的开发过程中,我们往往遇到这样的情形:
- 优先从A获取数据
- A中没有,则从B中取
- B中也没有,则从C中取
- 。。。。。。
你猜我为啥不往下写了,子子孙孙无穷匮也
预先准备
在展开讨论之前,我们先定义从各个地方获取数据的方法。当然,返回值不一定是字符串,在本例中,规定返回值为null
时认为数据不合法,应该从下一个地方获取数据:
private String getDataFromA() {
// 这是我从A获取的数据,数据不合法,返回空
return null;
}
private String getDataFromB() {
// 这是我从B获取的数据,巧了,数据也不合法,返回空
return null;
}
private String getDataFromC() {
return "这是我从C获取的数据";
}
private String getDataFromD() {
return "这是我从D获取的数据";
}
一般做法
在上述的情况下,我们通常的做法是(方式一):
String dataFromA = getDataFromA();
if (dataFromA != null) {
System.out.println(dataFromA);
} else {
String dataFromB = getDataFromB();
if (dataFromB != null) {
System.out.println(dataFromB);
} else {
// 到这真写不下去了
}
}
更有经验的做法是(方式二):
String dataFromA = getDataFromA();
if (dataFromA != null) {
System.out.println(dataFromA);
return;
}
String dataFromB = getDataFromB();
if (dataFromB != null) {
System.out.println(dataFromB);
return;
}
String dataFromC = getDataFromC();
if (dataFromC != null) {
System.out.println(dataFromC);
return;
}
String dataFromD = getDataFromD();
if (dataFromD != null) {
System.out.println(dataFromD);
return;
}
可以明显看到,方式二看起来比方式一更加优雅,看起来更加整洁。但是问题来了,无论方式一二,大量的篇幅都在用来判断数据是否合规。更重要的是,这样的逻辑可能在很多地方都会写。那么有没有方法能简化一下,让代码看起来更简洁?并且能够推广到大部分类似的情形?
引入链式调用与泛型
熟悉OkHttp
或者java的stream
操作的一定对链式调用不陌生,以stream
为例:
List<String> list = new ArrayList<>(Arrays.asList("1", "123", "342314", "12313", "21312"));
List<Integer> result = list.stream()
.map(Integer::valueOf)
.sorted(Integer::compare)
.collect(Collectors.toList());
System.out.println(result);
// [1, 123, 342314, 12313, 21312]
可以看到,通过引入stream
和lambda
、链式调用,我们轻松完成了:
字符串转数字->对数字排序->将结果整合为List
最重要的是,代码看起来简洁了不少
回到刚刚的话题,如何进行本文情形的优化?可以用链式调用的方法实现,为了方便,我直接定义静态内部类;当然也可以在一个单独的类中定义:
public static class DataGetter<T> {
private T result;
public DataGetter<T> order(Supplier<T> supplier) {
if (result != null) {
// 已经获取到了正常的数据,不用再执行一次获取操作了
// 这么写的原因是很多时候获取操作是很耗时间的,节约时间
return this;
}
// 执行一次数据获取
T t = supplier.get();
// 这里我认为非法数据为null
if (t != null) {
result = t;
}
// 这里的作用是返回它本身,得以继续链式调用
return this;
}
// 获取值
public T get() {
return result;
}
}
使用泛型的目的是为了让数据获取可以不仅仅局限与某一类数据。此时,要获取数据的方法可以直接优化为:
String result = new DataGetter<String>()
.order(() -> getDataFromA())
.order(() -> getDataFromB())
.order(() -> getDataFromC())
.order(() -> getDataFromD())
.get();
System.out.println(result);
// 这是我从C获取的数据
会按照order调用的顺序进行依次获取数据,直到获取合法的值,之后order调用则不会进行数据获取操作
优化与推广
加入默认值
有些时候,当所有数据来源均为非法,我们需要设置一个默认值:
public static class DataGetter<T> {
private T result;
private T defaultResult;
// 设置默认值
public DataGetter<T> defaultResult(T defaultResult) {
this.defaultResult = defaultResult;
return this;
}
public DataGetter<T> order(Supplier<T> supplier) {
if (result != null) {
// 已经获取到了正常的数据,不用再执行一次获取操作了
// 这么写的原因是很多时候获取操作是很耗时间的,节约时间
return this;
}
// 执行一次数据获取
T t = supplier.get();
// 这里我认为非法数据为null
if (t != null) {
result = t;
}
// 这里的作用是返回它本身,得以继续链式调用
return this;
}
// 获取值
public T get() {
// 获取值的时候判断一下result是否为空,非法且默认值不为空时返回默认值
if (result == null && defaultResult == null) {
return null;
}
if (result != null) {
return result;
}
return defaultResult;
}
}
设置默认值可以为以下操作:
String result = new DataGetter<String>()
.defaultResult("这是默认值,所有获取数据均非法时返回此值")
.order(() -> getDataFromA())
.order(() -> getDataFromB())
.get();
System.out.println(result);
// 这是默认值,所有获取数据均非法时返回此值
复杂的非法判断
很多时候数据非法不仅仅只是判断是否为null,往往需要更复杂的判断:
先修改getDataFromC()
方法:
private String getDataFromC() {
// 这是我从C获取的数据,虽然数据是非法的,但它不为null,气不气?
return "非法数据!";
}
对DataGetter
做修改:
public static class DataGetter<T> {
private T result;
private T defaultResult;
private Predicate<T> predicate;
// 传入的方法执行后如果为true,则认为合法
public DataGetter<T> notIllegal(Predicate<T> predicate) {
this.predicate = predicate;
return this;
}
// 设置默认值
public DataGetter<T> defaultResult(T defaultResult) {
this.defaultResult = defaultResult;
return this;
}
public DataGetter<T> order(Supplier<T> supplier) {
if (result != null) {
// 已经获取到了正常的数据,不用再执行一次获取操作了
// 这么写的原因是很多时候获取操作是很耗时间的,节约时间
return this;
}
// 执行一次数据获取
T t = supplier.get();
// 传入notIllegal,满足传入的条件才合法
if (predicate != null) {
if (predicate.test(t)) {
result = t;
}
}
// 没有传入notIllegal,则依旧认为为null不合法
else if (t != null) {
result = t;
}
// 这里的作用是返回它本身,得以继续链式调用
return this;
}
// 获取值
public T get() {
// 获取值的时候判断一下result是否为空,非法且默认值不为空时返回默认值
if (result == null && defaultResult == null) {
return null;
}
if (result != null) {
return result;
}
return defaultResult;
}
}
此时数据获取时:
String result = new DataGetter<String>()
.defaultResult("这是默认值,所有获取数据均非法时返回此值")
// 认为获取到的数据不为空且不等于"非法数据!"时合法
.notIllegal(data -> data != null && !"非法数据!".equals(data))
.order(() -> getDataFromA())
.order(() -> getDataFromB())
.order(() -> getDataFromC())
.order(() -> getDataFromD())
.get();
System.out.println(result);
// 这是我从D获取的数据
最终效果:加入适配器
当我们需要把得到的结果进行一次转化,从一类数据转化为另外一类数据时,继续修改DataGetter
类:
public static class DataGetter<T> {
private T result;
private T defaultResult;
private Predicate<T> predicate;
// 传入的方法执行后如果为true,则认为合法
public DataGetter<T> notIllegal(Predicate<T> predicate) {
this.predicate = predicate;
return this;
}
// 设置默认值
public DataGetter<T> defaultResult(T defaultResult) {
this.defaultResult = defaultResult;
return this;
}
public DataGetter<T> order(Supplier<T> supplier) {
if (result != null) {
// 已经获取到了正常的数据,不用再执行一次获取操作了
// 这么写的原因是很多时候获取操作是很耗时间的,节约时间
return this;
}
// 执行一次数据获取
T t = supplier.get();
// 传入notIllegal,满足传入的条件才合法
if (predicate != null) {
if (predicate.test(t)) {
result = t;
}
}
// 没有传入notIllegal,则依旧认为为null不合法
else if (t != null) {
result = t;
}
// 这里的作用是返回它本身,得以继续链式调用
return this;
}
// 获取值
public T get() {
// 获取值的时候判断一下result是否为空,非法且默认值不为空时返回默认值
if (result == null && defaultResult == null) {
return null;
}
if (result != null) {
return result;
}
return defaultResult;
}
// 传入一个适配器,将一类数据转为另外一类数据
public <R> R adapterGet(Function<T, R> function) {
if (function == null) {
return null;
}
return function.apply(get());
}
}
此时调用时:
DataGetter<String> getter = new DataGetter<String>()
.defaultResult("这是默认值,所有获取数据均非法时返回此值")
// 认为获取到的数据不为空且不等于"非法数据!"时合法
.notIllegal(data -> data != null && !"非法数据!".equals(data))
.order(() -> getDataFromA())
.order(() -> getDataFromB())
.order(() -> getDataFromC())
.order(() -> getDataFromD());
// 字符串转化为字符list
List<Character> chars = getter.adapterGet(data -> {
List<Character> result = new ArrayList<>();
for (int i = 0; i < data.length(); i++) {
result.add(data.charAt(i));
}
return result;
});
System.out.println(chars);
// [这, 是, 我, 从, D, 获, 取, 的, 数, 据]
// 将字符串转为字符串,但是转化后的字符串为原本字符传去除第一个字符
String result = getter.adapterGet(data -> data.substring(1));
System.out.println(result);
// 是我从D获取的数据
标题:Java中如何优雅的进行含优先级的数据获取(链式调用)
作者:汪沫远
地址:https://blog.wangzetong.online/articles/2024/08/24/1724495587003.html