2015-06-22 6 views
11

Voglio creare una classe in grado di eseguire un metodo finché non viene soddisfatta una condizione relativa al valore restituito.Java 8 - riprovare un metodo fino a quando una condizione è soddisfatta (a intervalli)

Dovrebbe essere qualcosa di simile

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

Il mio metodo poller guardare un po 'come questo() // seguente risposta 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; 
    } 
} 

Ma io sto avendo un momento difficile andare OPN da qui.
Come posso implementare un tentativo con un metodo generale fino a quando una condizione è soddisfatta?
Grazie.

+0

Bene, per quanto riguarda il metodo stesso, è possibile avere un riferimento all'oggetto di destinazione, al relativo Metodo riflesso e agli argomenti richiesti. Puoi anche incapsularlo in un lambda, ma sicuramente dipende da come viene passato quel metodo - esplicitamente o scoperto? per quanto riguarda i parametri? –

+0

@rails Se la risposta che ho fornito la soddisfa, per favore accettala. Non esitare se hai ulteriori domande. – GuiSim

risposta

18

Sì, questo può facilmente essere fatto in Java 7 e ancora più pulito utilizzando Java 8.

Il parametro per il metodo di method dovrebbe essere un java.util.function.Supplier<T> e il parametro per il metodo di until dovrebbe essere un java.util.function.Predicate<T>.

È quindi possibile utilizzare i riferimenti metodo o espressioni lambda per creare voi Poller in questo modo:

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

Come nota a margine, se avete intenzione di usare Java 8, mi consiglia di utilizzare invece di java.time.Duration un numero intero per rappresentare la durata del sondaggio e l'intervallo.

Suggerirei anche di esaminare https://github.com/rholder/guava-retrying che è una libreria che è possibile utilizzare. In caso contrario, potrebbe essere una buona fonte di ispirazione per la tua API poiché presenta una buona API fluente.

MODIFICA: Dopo l'aggiornamento alla domanda, ecco una semplice implementazione. Ho lasciato alcune parti da completare come 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; 
    } 
} 

uso Esempio:

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

Grazie. Ci sono molte gemme nella tua risposta.Aggiornato la domanda perché ho ancora bisogno di assistenza. Grazie. – Jeb

+1

@rails Vedi la mia modifica. – GuiSim

+0

Posso suggerire di condensare il ciclo 'while' a' while (! PollResultPredicate.test (pollMethod.get()) Thread.sleep (calculateDuration()); '. – ndm13

9

Invece di scrivere da soli, si potrebbe usare Awaitility?

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

mi piace il codice consise! –

1

Ecco una soluzione che utilizza Failsafe:

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

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

Abbastanza semplice come si può vedere, e gestisce lo scenario esatto.

Problemi correlati