LoginSignup
13
12

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-04-24

前提

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

こんなこともできる。

参考

13
12
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
12