逻辑判断和返回 vs. Optional
应用场景:当我们用到 if-elseif-else 的时候,可以考虑使用 Optional 语义。
举例说明:
if(id != null) {
return find1(id);
}
if(name != null) {
return find2(name);
}
if(type != null) {
return find3(type);
}
语义化
Supplier<?> chain = find(() -> find1(id), id)
.orElseGet(() -> find(() -> find2(name), name)
.orElse(() -> find3(type), type)));
Optional<Supplier<?>> find(Supplier s, Condtion... conditions) {
return Stream.of(conditions).anyMatch(c -> c == null) ? Optional.empty() : Optinal.of(s);
}
去掉了不必要的Supplier,让代码清晰化
static Optional<Supplier<?>> or(Optional<Supplier<?>> hl, Optional<Supplier<?>> hr) {
return hl.isPresent() ? hl : hr;
}
Supplier<?> chain =
or(
or(find(() -> find1(id), id),
find(() -> find2(name), name)),
find(() -> find3(type), type));
进一步思考,上述的场景可以简化为:
如果值存在,那么拿来映射成另一个可能的存在;否则,返回不可能的存在。
ofNullalbe(id)
.map(id -> find1(id))
.map(r -> nonNull(r) ? r : find2(name)))
.map(r -> nonNull(r) ? r : find3(type)))
.orElse(T t);
假如我们有Optional.orGet
函数,那就也可以这样实现
static <U> Optional<U> orGet(Supplier<? extends U> other) {
return isPresent() ? this : Optional.ofNullable(other.get());
}
ofNullalbe(id)
.map(id -> find1(id))
.orGet(() -> find2(name))
.orGet(() -> find3(type))
.orElse(T t);
不过,上面的实现方式缺失了 if not null then else
的表达,进一步改进:
ofNullalbe(id)
.map(id -> find1(id))
.orGet(() -> ofNullable(name).map(name -> find2(name)))
.orGet(() -> ofNullable(type).map(type -> find3(type)))
.orElse(T t);
static <U> Optional<U> orGet(Supplier<? extends Optional<U>> other) {
return isPresent() ? this : Optional.ofNullable(other.get());
}
完毕。
语句重构到表达式
if-else -> Optional
Optional<Rule> rule = ruleOf(id);
if(rule.isPresent()) {
return transform(rule.get());
} else {
throw new RuntimeException();
}
public Rule transform(Rule rule) {
return Rule.builder()
.withName("No." + rule.getId())
.build();
}
这是典型的语句可以重构到表达式的场景,关键是怎么重构呢?
第一步,调转if
。
Optional rule = ruleOf(id);
if(!rule.isPresent()) {
throw new RuntimeException();
}
return transform(rule.get());
第二步,Optional.map
函数
...
return rule.map(r -> transform(r)).get();
第三步,inline transform
函数
...
rule.map(r -> Rule.builder()
.withName("No." + r.getId())
.build()).get();
第四步,Optional.orElseThrow
函数
...
rule.map(r -> Rule.builder()
.withName("No." + r.getId())
.build())
.orElseThrow(() -> new RuntimeException());
第五步,注if
释语句中的throw new RuntimeException()
if(!rule.isPresent()) {
// throw new RuntimeException();
}
这时候发现语句中为空,即可将整个语句删除。可以考虑inline rule
。
ruleOf(id).map(r -> Rule.builder()
.withName("No." + r.getId())
.build())
.orElseThrow(() -> new RuntimeException());
完毕。
重复try...catch->Closure
// 结构性重复
if(meta.hasURI()) {
try {
return deciderOfURI.decide(attr);
} catch (CustomizedException e) {
//...
LOGGER.error(e);
} catch (Exception e) {
//...
LOGGER.error(e);
}
}
if(meta.hasAction()) {
try {
return deciderOfAction.decide(attr);
} catch (CustomizedException e) {
//...
LOGGER.error(e);
} catch (Exception e) {
//...
LOGGER.error(e);
}
}
这个结构性重复,一般想到的就是把表达式提成参数,但是由于表达式是预先求值的顺序,直接抽成参数意味着表达式求值时抛出的异常不能被捕获。所以使用functional parameter
重构手法。
第一步,extract method
pubic Decision tryDecide() {
if(meta.hasURI()) {
try {
return deciderOfURI.decide(attr);
} catch (CustomizedException e) {
//...
LOGGER.error(e);
} catch (Exception e) {
//...
LOGGER.error(e);
}
}
}
第二步,functional parameter
pubic Decision tryDecide(Supplier<Decision> decider) {
if(meta.hasURI()) {
try {
return decider.get();
} catch (CustomizedException e) {
//...
LOGGER.error(e);
} catch (Exception e) {
//...
LOGGER.error(e);
}
}
}
第三步,替换重复
// 结构性重复
if(meta.hasURI()) {
return tryDecide(() -> deciderOfAction.decide(attr));
}
if(meta.hasAction()) {
return tryDecide(() -> deciderOfURI.decide(attr));
}
完毕。
网友评论