这个循环代码能以某种方式简化吗?

时间:2015-09-27 23:55:55

标签: java java-8

我有一个问题......基本上我的代码很难看,我不喜欢它。我想知道是否有办法简化它(我使用java 8)

我有这些"代码块"按照这种模式,我在一个方法中有 5或6 ,所以这个方法看起来非常重复和丑陋。

循环完全相同,只是代码在内部变化。

有没有办法简化这个?

代码块示例

            String id = null;
            for (int i=0; i< NUM_CHECKS; i++) {

                // BEGIN VARIABLE CODE
                id = getPrice();
                if (id != null) break;
                // END VARIABLE CODE 

                // sleep between checks
                if (i < NUM_CHECKS -1) Thread.sleep(DELAY);
            }

实施例

            String id = null;
            for (int i=0; i< NUM_CHECKS; i++) {

                // BEGIN VARIABLE CODE
                id = getPrice();
                if (id != null) break;
                // END VARIABLE CODE 

                // sleep between checks
                if (i < NUM_CHECKS -1) Thread.sleep(DELAY);
            }

            for (int i=0; i< NUM_CHECKS; i++) {

                // BEGIN VARIABLE CODE
                x=x*2;
                if (x>25) break;
                // END VARIABLE CODE 

                // sleep between checks
                if (i < NUM_CHECKS -1) Thread.sleep(DELAY);
            } etc... a couple more blocks

4 个答案:

答案 0 :(得分:3)

如何编码抽象以包含所有样板?

class MyLoop
{
    private int numChecks;

    private int delay;

    public MyLoop(int numChecks, int delay) {...}

    public void loopAndSleep(MyTask task)
    throws InterruptedException
    {
        // Update: It is important to set properly the order of the looping conditions, 
        // to stop invoking hasEnded() as soon as i<numChecks==false (Thaks to Simon Eismann).
        for (int i=0; i<numChecks && !task.hasEnded(); i++)
        {
            if (i < numChecks -1)
            {
                Thread.sleep(DELAY);
            }
        }
    }
}

interface MyTask
{
    public boolean hasEnded();
}

因此,您可以通过以下方式替换程序中5-6个位置中的每一个:

new MyLoop(NUM_CHECKS, DELAY).loopAndSleep(new MyTask(){...});

通过正确扩展MyTask,您可以为其提供特定的状态变量。

答案 1 :(得分:3)

如果要在返回值可用之前尝试某些操作,可以执行以下操作(Java-8方式):

public static <T> Optional<T> retryWithDelay(int numberOfChecks, int delay, 
                               Supplier<Optional<T>> supplier) throws InterruptedException { 
    for(int i=0; i<numberOfChecks; i++) {
        if(i > 0)
            Thread.sleep(DELAY);
        Optional<T> result = supplier.get();
        if(result.isPresent()) return result;
    }
}

并像这样使用它:

String id = retryWithDelay(NUM_CHECKS, DELAY, () -> Optional.ofNullable(getPrice()))
                   .orElse(null);

或者,如果您因某些原因不喜欢选项,您可以坚持使用null

public static <T> T retryWithDelay(int numberOfChecks, int delay,
        Supplier<T> supplier) throws InterruptedException {
    for (int i = 0; i < numberOfChecks; i++) {
        if (i > 0)
            Thread.sleep(delay);
        T result = supplier.get();
        if (result != null)
            return result;
    }
    return null;
}

并像这样使用它:

String id = retryWithDelay(NUM_CHECKS, DELAY, () -> getPrice());

或使用方法参考:

String id = retryWithDelay(NUM_CHECKS, DELAY, this::getPrice);

请注意,带有x = 2*x的第二个示例更难,因为它具有一些可变状态。它可以像这样肮脏地解决:

AtomicInteger x = new AtomicInteger(1);
Integer result = retryWithDelay(NUM_CHECKS, DELAY, () -> {
    int val = x.get()*2;
    x.set(val);
    return val > 25 ? val : null;
});

但是我希望这个版本只是为了说明,而不是真正的代码。

还有一些更复杂的方法可能会滥用API,但允许更多的灵活性。您可以创建IntStream越来越多的数字,但它们可以在给定的延迟时间内使用:

public static IntStream delayedStream(int numberOfChecks, int delay) {
    return IntStream.range(0, numberOfChecks)
                    .peek(x -> {
                        if(x > 0) {
                            try {
                                Thread.sleep(delay);
                            } catch (InterruptedException e) {
                                // ignore
                            }
                        }
                    });
}

所以第一个问题现在可以解决:

String id = delayedStream(NUM_CHECKS, DELAY)
                  .mapToObj(x -> getPrice())
                  .filter(Objects::nonNull)
                  .findFirst().orElse(null);

第二个可以这样解决(假设初始x值为1):

int x = delayedStream(NUM_CHECKS, DELAY)
             .map(idx -> 1 << (idx+1))
             .filter(val -> val > 25)
             .findFirst().orElse(-1);

答案 2 :(得分:1)

您提供的结构称为&#34;轮询循环&#34;并且你是对的,编程风格很差,所有回复都包含相同的轮询循环。

使用事件要好得多。

查看&#34; getPrice()&#34;函数,到达更改返回值的位置,并在更改发生时创建事件。然后在你的代码中编写一个处理程序,并在处理程序中执行轮询循环成功后当前发生的所有事情。

答案 3 :(得分:0)

你可以使用递归来使循环可重用,但这只有在你经常使用循环时才有意义。

public void loopWithDelay(int numberOfChecks, int delay, Runnable r) { 
   if (numberOfChecks != 0) {
      r.run();
      loopWithDelay(numberOfChecks - 1, delay, r);
      Thread.sleep(DELAY);
   }
}

实际调用看起来像这样:

loopWithDelay(5, 1000, new Runnable() {
                  @Override
                  public void run() {
                     //Variable code goes here
                  }
             });

总的来说,您确定要在某个操作后等待DELAY秒还是每隔DELAY秒执行一次操作?

编辑: 我很笨,不需要递归,这也有效:

public void loopWithDelay(int numberOfChecks, int delay, Runnable r) { 
   for (int i = 0; i < numberOfChecks; i++) {
      r.run();
      if (i != numberOfChecks -1)
         Thread.sleep(DELAY);
   }
}