21

CompletionStage連鎖させたいメソッドがいくつかあります。問題は、最初のものの結果によって、次のものを実行する必要があるかどうかが決まることです。現在、これを達成する唯一の方法は、「特別な」引数を next に渡すことであるように思われるCompletionStageため、完全なコードは実行されません。例えば:

public enum SomeResult {
    RESULT_1,
    RESULT_2,
    RESULT_3
}

public CompletionStage<SomeResult> someMethod(SomeArgument someArgument) {

    return CompletableFuture.supplyAsync(() -> {
        // loooooong operation
        if (someCondition)
            return validValue;
        else
            return null;
    }).thenCompose(result -> {
        if (result != null)
            return someMethodThatReturnsACompletionStage(result);
        else
            return CompletableFuture.completedFuture(null);
    }).thenApply(result -> {
        if (result == null)
            return ChainingResult.RESULT_1;
        else if (result.someCondition())
            return ChainingResult.RESULT_2;
        else
            return ChainingResult.RESULT_3;
    });
}

コード全体が最初のものに依存するためsomeCondition(そうでない場合falseは結果が になりRESULT_1、そうでない場合はコード全体を実行する必要があります)、この構造は私には少し見苦しく見えます。2 番目 ( thenCompose(...)) と 3 番目 ( thenApply(...)) のメソッドを実行するかどうかを決定する方法はありますか?

4

3 に答える 3

18

次のように実行できます。

public CompletionStage<SomeResult> someMethod(SomeArgument someArgument) {
    CompletableFuture<SomeResult> shortCut = new CompletableFuture<>();
    CompletableFuture<ResultOfFirstOp> withChain = new CompletableFuture<>();

    CompletableFuture.runAsync(() -> {
        // loooooong operation
        if (someCondition)
            withChain.complete(validValue);
        else
            shortCut.complete(SomeResult.RESULT_1);
    });
    return withChain
        .thenCompose(result -> someMethodThatReturnsACompletionStage(result))
        .thenApply(result ->
                   result.someCondition()? SomeResult.RESULT_2: SomeResult.RESULT_3)
        .applyToEither(shortCut, Function.identity());
}

1 つではなくCompletableFuture2 つ作成し、異なる実行パスを表します。loooooong 操作は実行可能として送信され、意図的にこれらの 1 つを完了しCompletableFutureます。フォローアップ ステージは、満たされた条件を表すステージに連鎖され、両方の実行パスが最後のapplyToEither(shortCut, Function.identity())ステップで結合されます。

shortCutfuture はすでに最終結果の型を持っており、渡されたパスの結果である で完了します。これにより、操作全体がすぐに完了しますRESULT_1null最初のステージとショートカットの実際の結果値の間の依存関係が気に入らない場合は、次のように撤回できます。

public CompletionStage<SomeResult> someMethod(SomeArgument someArgument) {
    CompletableFuture<Object> shortCut = new CompletableFuture<>();
    CompletableFuture<ResultOfFirstOp> withChain = new CompletableFuture<>();

    CompletableFuture.runAsync(() -> {
        // loooooong operation
        if (someCondition)
            withChain.complete(validValue);
        else
            shortCut.complete(null);
    });
    return withChain
        .thenCompose(result -> someMethodThatReturnsACompletionStage(result))
        .thenApply(result ->
                   result.someCondition()? SomeResult.RESULT_2: SomeResult.RESULT_3)
        .applyToEither(shortCut.thenApply(x -> SomeResult.RESULT_1), Function.identity());
}

3 番目のステップが模範的ではなく、質問に示されているのとまったく同じように見える場合は、それをコード パスの結合ステップとマージできます。

public CompletionStage<SomeResult> someMethod(SomeArgument someArgument) {
    CompletableFuture<ResultOfSecondOp> shortCut = new CompletableFuture<>();
    CompletableFuture<ResultOfFirstOp> withChain = new CompletableFuture<>();

    CompletableFuture.runAsync(() -> {
        // loooooong operation
        if (someCondition)
            withChain.complete(validValue);
        else
            shortCut.complete(null);
    });
    return withChain
        .thenCompose(result -> someMethodThatReturnsACompletionStage(result))
        .applyToEither(shortCut, result -> result==null? SomeResult.RESULT_1:
            result.someCondition()? SomeResult.RESULT_2: SomeResult.RESULT_3);
}

次に、2 番目のステップであるsomeMethodThatReturnsACompletionStage呼び出しのみをスキップしますが、それでも中間ステップの長いチェーンを表すことができ、nullcheck による手動のスキップをロールアウトする必要なくすべてスキップされます。

于 2016-04-04T17:49:51.833 に答える