
本文探讨了在groovy中如何通过闭包(closure)来优雅地合并具有相似逻辑但条件判断不同的轮询方法,以减少代码冗余并提高可维护性。通过引入一个通用的`waituntil`方法,它接受一个返回布尔值的闭包作为条件检查器,并支持自定义重试间隔和最大重试次数,从而实现灵活且高效的条件等待机制,同时优化了潜在的垃圾回收开销。
在软件开发中,我们经常会遇到需要等待某个条件满足才能继续执行的场景。这些场景往往表现为一段轮询代码,其核心逻辑是在一个循环中反复检查某个状态,直到满足特定条件后退出。当存在多个这样的轮询逻辑,它们之间除了条件判断不同外,其余部分(如循环结构、等待机制、错误处理)高度相似时,代码冗余就会成为一个问题。
例如,考虑以下两个Groovy方法:
def someCondition = false
def method1() {
while(!someCondition) {
def connectionStatus = getConnectionStatus() // return 200, 404, etc.
if (connectionStatus == 200) {
someCondition = true
} else {
println "repeat until connection is 200"
sleep 15
}
}
}
def method2(){
while(!someCondition) {
def result = getResult() // A, B, C, etc.
if (result in ["A", "B", "C"]) {
someCondition = true
} else {
println "waiting until result is either A, B, or C"
sleep 15
result = getResult() // call again to check if result changed
}
}
}这两个方法都包含了一个while循环、一个内部的条件检查以及一个sleep调用。它们的区别仅在于if语句中的具体条件。直接复制粘贴并修改条件虽然可行,但会导致代码重复,降低可维护性。当需要修改等待逻辑(例如,改变等待时间或增加最大重试次数)时,必须修改所有相似的方法,这增加了出错的风险。
Groovy的闭包(Closure)提供了一种优雅的方式来抽象行为,使其作为参数传递给方法。我们可以利用这一特性,将上述两个方法中不同的条件判断逻辑封装成闭包,然后传递给一个通用的等待方法。
下面是一个名为waitUntil的通用方法,它接受一个闭包作为参数,该闭包负责执行实际的条件检查并返回一个布尔值(true表示条件满足,false表示条件不满足)。
/**
* 等待直到指定条件满足。
* @param sleep 每次重试之间的等待时间(毫秒)。
* @param maxRetries 最大重试次数。
* @param test 一个闭包,执行条件检查并返回布尔值。
*/
def waitUntil(int sleep = 1, int maxRetries = 10, Closure<Boolean> test) {
int retries = 0
while (retries++ < maxRetries && !test()) {
println "Failed at attempt $retries/$maxRetries, sleeping for $sleep ms before retrying"
Thread.sleep(sleep)
}
// 可以在此处添加逻辑来判断是否成功,例如返回一个布尔值
// return test() // 返回最终条件是否满足
}方法解析:
如何使用:
有了waitUntil方法,我们可以将之前的两个示例方法重构为简洁的调用:
// 假设 getConnectionStatus() 和 getResult() 是已定义的方法
// 对应 method1 的场景
waitUntil {
getConnectionStatus() == 200
}
// 对应 method2 的场景
waitUntil {
getResult() in ["A", "B", "C"]
}此外,waitUntil方法还支持自定义等待参数:
// 等待时间更长(100毫秒)
waitUntil(100) {
getResult() in ["A", "B", "C"]
}
// 等待时间更长(100毫秒),且只重试5次
waitUntil(100, 5) {
getResult() in ["A", "B", "C"]
}通过这种方式,我们成功地将重复的轮询逻辑抽象到一个通用方法中,极大地减少了代码冗余,并提高了灵活性。
在使用闭包进行条件检查时,需要注意一个潜在的性能问题:如果闭包内部每次执行都会创建新的对象(例如,每次都创建一个新的列表),在长时间运行或高速循环的任务中,这可能导致大量的临时对象被创建,从而增加垃圾回收的负担。
以上述 getResult() in ["A", "B", "C"] 为例,每次闭包执行时都会创建一个新的 ["A", "B", "C"] 列表。为了优化这一点,我们可以修改waitUntil方法,使其能够接受期望值作为参数,并将这些值传递给闭包。这样,列表或其他期望对象只需创建一次。
/**
* 等待直到指定条件满足,支持传递期望值到闭包。
* @param expectedResult 期望的结果值,可以是一个列表、单个值或其他任何闭包所需的参数。
* @param sleep 每次重试之间的等待时间(毫秒)。
* @param maxRetries 最大重试次数。
* @param test 一个闭包,执行条件检查并返回布尔值。它接受 expectedResult 作为参数。
*/
def waitUntil(Object expectedResult, int sleep = 1, int maxRetries = 10, Closure<Boolean> test) {
int retries = 0
while (retries++ < maxRetries && !test(expectedResult)) { // 将 expectedResult 传递给闭包
println "Failed at attempt $retries/$maxRetries, sleeping for $sleep ms before retrying"
Thread.sleep(sleep)
}
// return test(expectedResult) // 返回最终条件是否满足
}使用优化后的方法:
// 期望结果列表只创建一次
waitUntil(["A", "B", "C"]) { expected -> // 闭包现在接受一个参数
getResult() in expected // 使用传入的 expected 参数
}
// 也可以传递单个值
waitUntil(200) { expectedStatus ->
getConnectionStatus() == expectedStatus
}在这个优化版本中,["A", "B", "C"] 列表只在 waitUntil 方法被调用时创建一次,然后作为参数传递给闭包。闭包在每次执行时,都使用这个已经存在的列表进行比较,避免了重复创建对象的开销。
通过利用Groovy的闭包特性,我们可以将相似的轮询等待逻辑进行高度抽象和合并,从而实现:
注意事项:
通过上述方法,Groovy开发者可以编写出更简洁、更健壮、更易于维护的轮询等待代码。
以上就是Groovy中利用闭包优雅合并相似轮询方法的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号