2016-04-04 13 views
11

Tôi có một số phương pháp CompletionStage mà tôi muốn chuỗi. Vấn đề là kết quả của việc đầu tiên sẽ xác định xem những cái tiếp theo có nên được thực hiện hay không. Ngay bây giờ, cách duy nhất để đạt được điều này dường như chuyển các đối số "đặc biệt" tới CompletionStage tiếp theo để nó không thực thi mã đầy đủ. Ví dụ:Chained một số CompletionStage chỉ khi một điều kiện đạt được

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

Kể từ khi toàn bộ mã phụ thuộc vào someCondition đầu tiên (nếu nó false sau đó kết quả sẽ là RESULT_1, nếu không thì toàn bộ mã nên được thực hiện) xây dựng này trông hơi xấu xí với tôi. Có cách nào để quyết định xem các phương thức 2 (thenCompose(...)) và thứ 3 (thenApply(...)) có nên được thực thi không?

Trả lời

7

Bạn có thể làm điều đó như thế này:

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

Thay vì một CompletableFuture chúng ta tạo ra hai, đại diện cho những con đường thực hiện khác nhau chúng ta có thể thực hiện. Các hoạt động loooooong được gửi như là runnable sau đó và sẽ cố tình hoàn thành một trong những CompletableFuture. Các giai đoạn tiếp theo được liên kết với giai đoạn biểu thị điều kiện đã hoàn thành, sau đó cả hai đường dẫn thực hiện đều tham gia vào bước applyToEither(shortCut, Function.identity()) cuối cùng.

Tương lai shortCut đã là loại kết quả cuối cùng và sẽ được hoàn thành với RESULT_1, kết quả của đường dẫn đi qua null của bạn, điều này sẽ dẫn đến việc hoàn thành toàn bộ hoạt động. Nếu bạn không thích sự phụ thuộc giữa các giai đoạn đầu tiên và giá trị kết quả thực tế của các short-cut, bạn có thể rút nó như thế này:

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

Nếu bước thứ ba của bạn không phải là gương mẫu nhưng trông giống hệt như thể hiện trong câu hỏi, bạn có thể kết hợp nó với các bước con đường mã tham gia:

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

sau đó chúng tôi chỉ bỏ qua bước thứ hai, someMethodThatReturnsACompletionStage gọi, nhưng điều đó vẫn có thể đại diện cho một chuỗi dài các bước trung gian, tất cả bỏ qua mà không sự cần thiết phải tung ra một thủ thuật bỏ qua thông qua nullcheck.

+0

Nó hoạt động, cảm ơn! Theo cùng một mẫu (tạo ra một số 'CompletableFuture' và sử dụng' applyToEither (...) ') thì có thể mở rộng nó sang một vài đường dẫn, tôi có đúng không? – Pelocho

+1

Có, bạn có thể mở rộng nó đến nhiều đường dẫn nhưng phải cẩn thận để giữ cho mã kết quả có thể duy trì được. Có lẽ nó giúp đóng gói logic của một nhánh thành một phương thức tiện ích mà bạn có thể sử dụng nhiều lần. – Holger

0

Vì mục đích hoàn chỉnh, tôi thêm câu trả lời mới

Mặc dù giải pháp được đề xuất bởi @Holger hoạt động tuyệt vời nhưng điều đó thật lạ lùng đối với tôi. Các giải pháp tôi đã sử dụng liên quan đến việc tách dòng khác nhau trong các cuộc gọi phương pháp khác nhau và chaining được kết chúng với 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); 
} 

LƯU Ý: Tôi đã thay đổi thuật toán từ các câu hỏi trong lợi ích của một câu trả lời hoàn chỉnh hơn

Tất cả các hoạt động dài có thể có khả năng được bao bọc bên trong một số khác CompletableFuture.supplyAsync với ít nỗ lực

Các vấn đề liên quan