我有一个问题......基本上我的代码很难看,我不喜欢它。我想知道是否有办法简化它(我使用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
答案 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);
}
}