2015-06-22 3 views
11

Ich möchte eine Klasse erstellen, die eine Methode ausführen kann, bis eine Bedingung über den Rückgabewert erfüllt ist.Java 8 - Wiederholen Sie eine Methode, bis eine Bedingung erfüllt ist (in Intervallen)

Es sollte so etwas wie dieses

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

Meine Methode poller anschaut etwas wie folgt aus() // folgende GuiSim Antwort

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

Aber ich habe eine harte Zeit gehen opn von hier mit.
Wie kann ich einen Wiederholungsversuch auf eine allgemeine Methode implementieren, bis eine Bedingung erfüllt ist?
Danke.

+0

Nun, in Bezug auf die Methode selbst, könnten Sie einen Verweis auf das Zielobjekt, seine reflektierte Methode und die erforderlichen Argumente haben. Sie könnten es auch in einem Lambda einkapseln, aber es hängt definitiv davon ab, wie diese Methode übergeben wird - explizit oder entdeckt? Was ist mit Parametern? –

+0

@rails Wenn die Antwort, die ich Ihnen gegeben habe, Sie zufriedenstellt, akzeptieren Sie sie bitte. Zögern Sie nicht, wenn Sie weitere Fragen haben. – GuiSim

Antwort

18

Ja, das kann leicht in Java 7 und noch sauberer mit Java 8.

Der Parameter für Ihre method Methode sollte ein java.util.function.Supplier<T> und der Parameter auf Ihre until Methode getan werden sollte, ein java.util.function.Predicate<T> sein.

Sie können dann Methode Referenzen oder Lambda-Ausdrücke verwenden, um Sie wie so POLLER zu erstellen:

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

Als Seite beachten, wenn Sie Java verwenden werden 8, empfehle ich java.time.Duration Verwendung würde statt eine ganze Zahl zur Darstellung der Poll-Dauer und des Intervalls.

Ich würde auch empfehlen, in https://github.com/rholder/guava-retrying suchen, die eine Bibliothek ist, die Sie vielleicht verwenden könnten. Wenn nicht, könnte es eine gute Inspiration für Ihre API sein, da sie eine nette flüssige API bietet.

EDIT: Nach dem Update auf die Frage, hier ist eine einfache Implementierung. Ich habe einige Teile für dich als TODOs hinterlassen.

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

Beispiel Verwendung:

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

Danke. Es gibt viele Edelsteine ​​in deiner Antwort.Die Frage wurde aktualisiert, weil ich noch Hilfe brauche. Vielen Dank. – Jeb

+1

@rails Siehe meine Bearbeitung. – GuiSim

+0

Darf ich vorschlagen, die 'while' -Schleife auf' while (! PollResultPredicate.test (pollMethod.get()) zu kondensieren. Thread.sleep (calculateDuration());.. – ndm13

9

Statt des Schreibens dieses selbst, könnte man Awaitility benutzen?

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

Ich mag die Consise-Code! –

1

Hier ist eine Lösung mit Failsafe:

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

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

recht einfach, wie Sie sehen können, und kümmert sich um Ihr genaues Szenario.