Help us understand the problem. What is going on with this article?

流れるようなインタフェースで条件分岐

More than 3 years have passed since last update.

前提

  • 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");

こんなこともできる。

参考

7tsuno
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away