2016-04-04 3 views
11

私は連鎖したいいくつかの方法を持っています:CompletionStage問題は、最初のものの結果が次のものが実行されるべきかどうかを判断することです。現在、これを達成する唯一の方法は、特別な引数を次のCompletionStageに渡しているように見えるので、完全なコードは実行されません。たとえば:コード全体以来条件が成立した場合にのみ、いくつかの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になります)この構成は、私には少し醜いです。 2nd(thenCompose(...))と3rd(thenApply(...))のメソッドを実行する必要があるかどうかを判断する方法はありますか?

答えて

7

あなたはこのようにそれを行うことができます。

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 CompletableFuture我々はかかるかもしれません異なる実行パスを表す2つを作成します。 loooooong操作はrunnableとして提出され、意図的にこれらのいずれかを完了しますCompletableFuture。フォローアップステージは、満たされた条件を表すステージに連鎖され、最後に両方の実行パスが最後のapplyToEither(shortCut, Function.identity())ステップに参加します。

shortCut未来はすでに最終的な結果の種類を持っていると、RESULT_1で全体の動作を即座に完了したことを原因になりますあなたのnull通過パスの結果を完了します。あなたの第三段階は、例示ではなかったが、まったく同じように見える場合

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()); 
} 

:あなたが最初のステージ間の依存関係とショートカットの実際の結果値が気に入らない場合、あなたはこのようにそれを撤回することができます質問に、あなたはコードパス接合工程とそれをマージすることができます:

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); 
} 

は、我々は唯一の第二段階、someMethodThatReturnsACompletionStage呼び出しをスキップするが、それはまだ中間段階の長鎖のために立つことができ、すべてがなくてスキップnullcheckを使用して手動でスキップする必要があります。完全@Holgerによって提案された解決策は、素晴らしい作品、それは私にはちょっと奇妙だが、私は新しい答え

を追加しているの便宜上

+0

ありがとうございます!同じパターン(いくつかの 'CompletableFuture'を作成し、' applyToEither(...) 'を使用)に続いて、それをいくつかのパスに拡張することができます。 – Pelocho

+1

はい、複数のパスに拡張することができますが、結果のコードを保守しやすくするために注意する必要があります。おそらく、ブランチのロジックを複数回使用できるユーティリティメソッドにカプセル化すると便利です。 – Holger

0

。私が使用してきたソリューションは、さまざまなメソッド呼び出しで異なるフローを分離し、thenComposeでそれらをチェーン含まれます

public enum SomeResult { 
    RESULT_1, 
    RESULT_2, 
    RESULT_3 
} 

public CompletionStage<SomeResult> someMethod(SomeArgument someArgument) { 

    return CompletableFuture.supplyAsync(() -> { 
     // loooooong operation 
     if (someCondition) 
      return operateWithValidValue(value); 
     else 
      return CompletableFuture.completedValue(ChainingResult.RESULT_1); 
    }) 
     .thenCompose(future -> future); 

public CompletionStage<SomeResult> operateWithValidValue(... value) { 
    // more loooong operations... 
    if (someCondition) 
     return CompletableFuture.completedValue(SomeResult.RESULT_2); 
    else 
     return doFinalOperation(someOtherValue); 
} 

public CompletionStage<SomeResult> doFinalOperation(... value) { 
    // more loooong operations... 
    if (someCondition) 
     return CompletableFuture.completedValue(SomeResult.RESULT_2); 
    else 
     return CompletableFuture.completedValue(SomeResult.RESULT_3); 
} 

:私はより完全な答え

のために質問からアルゴリズムを変更しました

すべての長い操作は、少しでも努力して別のCompletableFuture.supplyAsyncの中に潜んでいる可能性があります。

関連する問題