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

Но мне сложно с этим справиться.
Как я могу реализовать повтор общего метода до тех пор, пока условие не будет выполнено?
Спасибо.

Ответ 1

Да, это можно легко сделать в Java 7 и даже более чистым, используя Java 8.

Параметр вашего метода method должен быть java.util.function.Supplier<T>, а параметр для вашего метода until должен быть java.util.function.Predicate<T>.

Затем вы можете использовать ссылки на методы или лямбда-выражения, чтобы создать вас таким же образом:

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.

EDIT: После обновления к вопросу, вот простая реализация. Я оставил некоторые части для вас в качестве 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);
    }
}

Ответ 2

Вместо того, чтобы писать это самостоятельно, не могли бы вы использовать Awaitility?

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

Ответ 4

Здесь решение с использованием Failsafe:

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

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

Довольно просто, как вы можете видеть, и обрабатывает ваш точный сценарий.