一、背景
在业务需求中,常常会碰到这样的场景。
假设要执行逻辑 A 前需要校验条件 B 和 C,而 B 和 C 需要执行异步操作。
常规思路: 将 B 和 C 串行执行网络请求,然后通过 C 接口的回调执行 A 的逻辑。
缺点: 职责不明确,代码耦合度高,回调嵌套深,不利于扩展。如果多处使用,则代码重复的问题可以通过抽取公用方法来解决。
二、解决方案
通过引入 拦截器 ,将每个条件作为单独的拦截器进行拦截判断。
缺点: 单独的一系列拦截器不存在关联关系,不能在拦截器中处理异步的逻辑。
改进方案: 通过引入 责任链模式,将条件 B 和 C 串联起来,依次校验是否满足 B 和 C 的拦截条件,如果不满足,则执行 A 的逻辑。
优点:
- 条件 B 和 条件 C 的校验逻辑遵循单一职责。
- 易于扩展(可以在B、C条件之后校验条件D)
- 没有依赖顺序,可以随意组合顺序。
- 代码复用率提高。
三、代码实现
/**
* 加入拦截器数组作为判断条件
*/
fun doIntercept(vararg interceptors: Interceptor, failed: () -> Unit, success: () -> Unit) {
if (interceptors.isEmpty()) {
success()
return
}
// 将拦截器链式调用
interceptors.forEachIndexed { index, interceptor ->
if (index == interceptors.size - 1) {
interceptor.setChildInterceptor(null)
} else {
interceptor.setChildInterceptor(interceptors[index + 1])
}
}
interceptors.first().intercept(success, failed)
}
interface Interceptor {
// 关联下一个拦截器
fun setChildInterceptor(interceptor: Interceptor?)
fun intercept(success: (() -> Unit)?, failed: (() -> Unit)?)
}
四、示例
// ******** Demo ********
fun test() {
// 判断条件1
val interceptor1 = Interceptor1()
// 判断条件2
val interceptor2 = Interceptor2()
doIntercept(interceptor1, interceptor2, failed = {
// 失败逻辑(即被拦截了)
}, success = {
// 成功逻辑(没有被拦截)
})
}
class Interceptor1() : Interceptor {
var mInterceptor: Interceptor? = null
override fun setChildInterceptor(interceptor: Interceptor?) {
this.mInterceptor = interceptor
}
override fun intercept(success: (() -> Unit)?, failed: (() -> Unit)?) {
// 异步回调代码
val asynCallback = {
// 回调成功的逻辑
if (callbackSuccess) {
if (interceptor == null) success?.invoke() else interceptor?.intercept(success, failed)
} else {
failed?.invoke()
}
}
}
}
class Interceptor2 : Interceptor {
var mInterceptor: Interceptor? = null
override fun setChildInterceptor(interceptor: Interceptor?) {
this.mInterceptor = interceptor
}
override fun intercept(success: (() -> Unit)?, failed: (() -> Unit)?) {
// 异步回调代码
val asynCallback = {
// 回调成功的逻辑
if (callbackSuccess) {
if (interceptor == null) success?.invoke() else interceptor?.intercept(success, failed)
} else {
failed?.invoke()
}
}
}
}