2015-06-22 40 views
11

我想创建一个类,该类可以运行方法,直到满足返回值的条件。Java 8 - 重试一个方法,直到满足条件(间隔)

它应该是这个样子

methodPoller.poll(pollDurationSec, pollIntervalMillis) 
      .method(dog.bark()) 
      .until(dog -> dog.bark().equals("Woof")) 
      .execute(); 

我的方法轮询看起来有点像这样()//以下GuiSim答案

public class MethodPoller { 
    Duration pollDurationSec; 
    int pollIntervalMillis; 


    public MethodPoller() { 
    } 

    public MethodPoller poll(Duration pollDurationSec, int pollIntervalMillis) { 
     this.pollDurationSec = pollDurationSec; 
     this.pollIntervalMillis = pollIntervalMillis; 
     return this; 
    } 

    public <T> MethodPoller method(Supplier<T> supplier) { 

     return this; 
    } 

    public <T> MethodPoller until(Predicate<T> predicate) { 

     return this; 
    } 
} 

,但我有一个很难从这里OPN去。
如何在一个条件满足之前实现对一般方法的重试?
谢谢。

+0

那么,关于方法本身,您可以引用目标对象,其反映的方法和所需的参数。你也可以将它封装在一个lambda中,但是这绝对取决于该方法是如何传递的 - 显式地或者发现的?什么参数? –

+0

@rails如果我提供的答案满足您,请接受它。如果您还有其他问题,请不要犹豫。 – GuiSim

回答

18

是的,这可以很容易地在Java 7中完成,使用Java 8

参数更清洁你的method方法应该是一个java.util.function.Supplier<T>和参数,以此来until方法应该是java.util.function.Predicate<T>

然后可以使用方法引用或lambda表达式,以创建轮询像这样:

myMethodPoller.poll(pollDurationInteger, intervalInMillisecond) 
      .method(payment::getStatus) 
      .until (paymentStatus -> paymentStatus.getValue().equals("COMPLETED")) 
      .execute(); 

作为一个侧面说明,如果你打算使用Java 8,我推荐使用java.time.Duration代替一个表示轮询持续时间和间隔的整数。

我也建议您查看https://github.com/rholder/guava-retrying这是一个您可能可以使用的库。如果不是,它可能是您的API的一个很好的灵感,因为它具有流畅的API。

编辑: 继问题更新之后,下面是一个简单的实现。我已经留下了一些部分给你完成TODO。

import java.time.Duration; 
import java.util.function.Predicate; 
import java.util.function.Supplier; 

public class MethodPoller<T> { 

    Duration pollDurationSec; 
    int pollIntervalMillis; 

    private Supplier<T> pollMethod = null; 
    private Predicate<T> pollResultPredicate = null; 

    public MethodPoller() { 
    } 

    public MethodPoller<T> poll(Duration pollDurationSec, int pollIntervalMillis) { 
     this.pollDurationSec = pollDurationSec; 
     this.pollIntervalMillis = pollIntervalMillis; 
     return this; 
    } 

    public MethodPoller<T> method(Supplier<T> supplier) { 
     pollMethod = supplier; 
     return this; 
    } 

    public MethodPoller<T> until(Predicate<T> predicate) { 
     pollResultPredicate = predicate; 
     return this; 
    } 

    public T execute() 
    { 
     // TODO: Validate that poll, method and until have been called. 

     T result = null; 
     boolean pollSucceeded = false; 
     // TODO: Add check on poll duration 
     // TODO: Use poll interval 
     while (!pollSucceeded) { 
      result = pollMethod.get(); 
      pollSucceeded = pollResultPredicate.test(result); 
     } 

     return result; 
    } 
} 

使用示例:

import static org.junit.Assert.assertTrue; 
import java.util.UUID; 
import org.junit.Test; 

public class MethodPollerTest 
{ 

    @Test 
    public void test() 
    { 
     MethodPoller<String> poller = new MethodPoller<>(); 
     String uuidThatStartsWithOneTwoThree = poller.method(() -> UUID.randomUUID().toString()) 
                .until(s -> s.startsWith("123")) 
                .execute(); 
     assertTrue(uuidThatStartsWithOneTwoThree.startsWith("123")); 
     System.out.println(uuidThatStartsWithOneTwoThree); 
    } 
} 
+1

谢谢。答案中有许多宝石。更新了这个问题,因为我仍然需要一些帮助。谢谢。 – Jeb

+1

@rails请参阅我的编辑。 – GuiSim

+0

我可以建议凝结while循环while(!pollResultPredicate.test(pollMethod.get())Thread.sleep(calculateDuration());'。 – ndm13

9

代替此写自己,你可以使用Awaitility

await() 
    .atMost(3, SECONDS) 
    .until(dog::bark, equalTo("woof")); 
+0

我喜欢consise代码! –

1

下面是使用Failsafe一个解决方案:

RetryPolicy retryPolicy = new RetryPolicy() 
    .retryIf(bark -> bark.equals("Woof")) 
    .withMaxDuration(pollDurationSec, TimeUnit.SECONDS); 
    .withDelay(pollIntervalMillis, TimeUnit.MILLISECONDS); 

Failsafe.with(retryPolicy).get(() -> dog.bark()); 

,你可以看到非常简单,并且处理您确切的情况。