Элегантный способ повторять что-то с интервалами в регулярно вызываемой функции - PullRequest
0 голосов
/ 08 ноября 2019

Я регулярно пишу программное обеспечение, которое использует платформу, в которой метод обратного вызова вызывается много раз в секунду, но внутри этого обратного вызова я хочу выполнять какую-то операцию каждые N секунд. Так что я делаю это много:

int lastMethodCallTime = 0;
int methodRunIntervalMs = 5000;
void methodThatGetsCalledManyTimesPerSecond(){
   int currentTime = getCurrentTime();
   if(currentTime - lastMethodCallTime >= methodRunIntervalMs){
      lastMethodCallTime = currentTime;
      myMethod();
   }
}

Но брутто. Это уродливо, требует объявления внешних переменных и очень подвержено ошибкам (много раз я забывал lastMethodCallTime = currentTime;, а потом удивлялся, почему он вызывался так много раз). И если у вас есть несколько методов, которые вызываются с разными интервалами, тогда это безобразие просто умножается.

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

1 Ответ

1 голос
/ 10 ноября 2019

Этот ответ не очень общий. Я даю предложение на Java, и вы можете посмотреть, сможете ли вы адаптировать его к другим языкам, которые вы используете. Я использую некоторые функции Java.

Также я не могу написать ваш код более красиво, чем то, что вы уже сделали. Я предлагаю применить то, что написал Бьярн Страуструп: код может быть настолько уродливым, насколько это необходимо, до тех пор, пока вы можете скрыть это за красивым интерфейсом. Это позволит вам написать пример кода следующим образом:

CallFrequencyController myCallFrequencyController
        = new CallFrequencyController(5, TimeUnit.SECONDS, this::myMethod);

void methodThatGetsCalledManyTimesPerSecond() {
    myCallFrequencyController.call();
}

Здесь я скрываю уродливый код:

class CallFrequencyController {

    Instant nextCallTime = Instant.now();
    final long methodRunIntervalNanos;
    final Runnable methodToCall;

    public CallFrequencyController(int frequency, TimeUnit frequencyUnit, Runnable methodToCall) {
        methodRunIntervalNanos = frequencyUnit.toNanos(frequency);
        this.methodToCall = methodToCall;
    }

    public void call() {
        Instant now = Instant.now();
        if (now.isBefore(nextCallTime)) {
            return;
        }

        methodToCall.run();

        nextCallTime = now.plusNanos(methodRunIntervalNanos);
    }

}

Вы можете разработать этот класс один раз и тщательно его протестировать. , а затем вам больше не нужно беспокоиться о том, чтобы забыть обновить время последнего или следующего вызова или написать другие ошибки.

Если вам нужно передать аргумент myMethod и вернуть результат из него в вашу платформу,вам нужно, чтобы ваш класс был общим. Это еще одно осложнение, но опять же: в основном оно скрыто за красивым интерфейсом. Да, интерфейс универсальный, но это хорошо, и, в частности, код, использующий его, выглядит красиво. Сложность скрыта за этим.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...