前提
- java8以降
問題となる条件分岐
よく見るようなこんな処理。
// 文字列が空ではない、かつアルファベット小文字である場合
// かつ
// 処理区分が登録・変更のいずれかである場合
if ((StringUtils.isNotEmpty(str) && StringUtils.isAlpha(str)
&& StringUtils.isAllLowerCase(str))
&& (processDivision.isRegister() || processDivision.isChange())) {
// なんか処理
}
コードをぱっと見るとどこまでが条件なのか分かりづらいし、括弧を付ける位置とかミスもしやすい。
このくらいならまだマシかもしれないが、条件が増えてきたら絶望する...
流れるようなインタフェースで解決
以下のようなクラスを作る。
Assume.java
public final class Assume<T> {
private T obj;
public static <T> Assume<T> that(T obj) {
Assume<T> assume = new Assume<>();
assume.obj = obj;
return assume;
}
public AnyMatch<T> satisfiesAnyOf(Predicate<T> method) {
AnyMatch<T> anyOf = new AnyMatch<>(obj, method);
return anyOf;
}
public AllMatch<T> satisfiesAllOf(Predicate<T> method) {
AllMatch<T> allOf = new AllMatch<>(obj, method);
return allOf;
}
}
AnyMatch.java
public final class AnyMatch<T> {
private T obj;
private boolean match;
protected AnyMatch(T obj, Predicate<T> checkMethod) {
this.match = checkMethod.test(obj);
this.obj = obj;
}
public AnyMatch<T> or(Predicate<T> checkMethod) {
if (match) {
return this;
}
this.match = checkMethod.test(obj);
return this;
}
public boolean check() {
return match;
}
}
AllMatch.java
public class AllMatch<T> {
private T obj;
private boolean match;
protected AllMatch(T obj, Predicate<T> checkMethod) {
this.match = checkMethod.test(obj);
this.obj = obj;
}
public AllMatch<T> and(Predicate<T> checkMethod) {
if (!match) {
return this;
}
this.match = checkMethod.test(obj);
return this;
}
public boolean check() {
return match;
}
}
この3つを用いると冒頭の処理は以下のように書けます。
// 文字列が空ではない、かつアルファベット小文字である場合
// かつ
// 処理区分が登録・変更のいずれかである場合
if (Assume.that(str)
.satisfiesAllOf(StringUtils::isNotEmpty)
.and(StringUtils::isAlpha)
.and(StringUtils::isAllLowerCase).check()
&& Assume.that(processDivision)
.satisfiesAnyOf(ProcessDivision::isRegister)
.or(ProcessDivision::isChange).check()) {
// なんか処理
}
記述は長くなったが、何をしているかがわかりやすくなった。はず。
さらに改良
if文でも三項演算子でもない第三のIf文を書いてみる
の記事を参考にさらに改良。
AnyMatch.java
public final class AnyMatch<T> {
...
public <A> Else<A> then(Supplier<A> a1) {
return (Supplier<A> a2) -> match ? a1.get() : a2.get();
}
}
を追加し、
Else.java
public interface Else<A> {
A orElse(Supplier<A> a2);
}
を用意すれば
String hoge = Assume.that(str)
.satisfiesAllOf(StringUtils::isNotEmpty)
.and(StringUtils::isAlpha)
.and(StringUtils::isAllLowerCase).then(() -> "aaa").orElse(() -> "bbb");
こんなこともできる。