Kotlin + Flow 实现责任链模式的4种案例

责任链模式在开发中,用得还算是比较广泛的,掌握它的使用,对于架构或者写框架都有很大帮助

一、前言

责任链模式通过构建链式处理结构实现请求的动态传递,其核心优势在于降低系统耦合度与提升扩展性, 它主要优点是:

  1. 请求发送者与处理者解耦
  2. 动态扩展能力强
  3. 职责分离明确
  • 在Android中,用到责任链的主要框架有哪些:
  1. ViewGroup的事件分发责任链
  2. OkHttp的拦截器责任链
  3. ‌Glide的资源加载责任链
  • 但是它们涉及到的责任链都是传统责任链模式写法。

我们知道Flow也可以链式调用,那么采用Flow实现责任链模式相比于传统方式实现责任链模式有哪些优缺点呢?

Flow实现责任链与传统责任链对比:

特性 Flow实现方案 传统责任链模式
异步支持 ✅ 内置协程异步处理 ❌ 需手动实现线程池
背压管理 ✅ 自动流速控制 ❌ 需额外实现
链式组合 ✅ 函数式操作符连接 ✅ 对象指针连接
错误处理 ✅ 统一catch捕获 ❌ 需逐级传递异常

本文重点介绍 :
Kotlin + Flow 实现责任链模式的4种案例,具体看它怎么实现

二、案例一

我们有个需求,是3个请求,需要分三步走:

  1. 第一步输入账号登录,如果没有认证,认证完再进行第二步
  2. 在第二步进行各种校验,校验成功后走第三步,校验错误直接抛异常
  3. 第三步进行一些记录。

具体代码实现:

  1. 定义好处理器接口
kotlin 复制代码
interface WXChainHandler<T> {
    suspend fun process(input: T): Flow<T>
}
  1. 实现三个步骤的各自职责
kotlin 复制代码
class AuthHandler : WXChainHandler<String> {
    override suspend fun process(input: String): Flow<String> = flow {
        println("AuthHandler 在处理")
        if (input.startsWith("auth:")) {
            emit(input.replace("auth:", "") + "[认证完成]")
        } else {
            emit(input)
        }
    }
}

class ValidationHandler : WXChainHandler<String> {
    override suspend fun process(input: String): Flow<String> = flow {
        println("ValidationHandler 在处理")
        require(input.length >= 3) { "输入长度不足" }
        emit("校验通过:$input")
    }
}

class LogHandler : WXChainHandler<String> {
    override suspend fun process(input: String): Flow<String> = flow {
        println("处理日志: $input")
        emit(input)
    }
}

3、通过Flow构建调用责任链,

通过 fold + flatMapConcat 将处理器串联成链,每个处理器输出作为下一个输入,

通过 process() 返回 Flow 支持异步操作。

统一使用Flowcatch 操作符统一捕获全链异常
背压支持 :通过使用 FlowflatMapConcat 自动处理生产者-消费者速度差异。

kotlin 复制代码
class WXChainProcessor<T>(private val handlers: List<WXChainHandler<T>>) {

    @OptIn(FlowPreview::class)
    fun execute(request: T): Flow<T> {
        return handlers.fold(flow { emit(request) }) { acc, handler ->
            acc.flatMapConcat { handler.process(it) }
        }.catch { e ->
            emit("处理异常: ${e.message}" as T)
        }
    }
}

调用方:

scss 复制代码
fun main() {
    runBlocking {
        val processor = WXChainProcessor(
            listOf(
                AuthHandler(), ValidationHandler(), LogHandler()
            )
        )
        listOf("auth:test", "short", "normal", "df").forEach { req ->
            processor.execute(req).flowOn(Dispatchers.IO).collect {
                println("最终结果: $it")
            }
            println("--------")
        }
    }
}

可以看到输出结果:

ruby 复制代码
    AuthHandler 在处理
    ValidationHandler 在处理
    处理日志: 校验通过:test[认证完成]
    最终结果: 校验通过:test[认证完成]
    --------
    AuthHandler 在处理
    ValidationHandler 在处理
    处理日志: 校验通过:short
    最终结果: 校验通过:short
    --------
    AuthHandler 在处理
    ValidationHandler 在处理
    处理日志: 校验通过:normal
    最终结果: 校验通过:normal
    --------
    AuthHandler 在处理
    ValidationHandler 在处理
    最终结果: 处理异常: 输入长度不足
    --------

从上面日志里面我们可以得出结论:

  1. 里面每一个都在处理,处理完了的结果给到下一个再来处理。
  2. 这里的每一个处理对于客户端来说可能是一个耗时操作,可以通过配置在异步里面。
  3. 里面每一个都在处理,处理完的结果都是新的结果。最终给出。

那么责任链模式中还有一种最经典的,IO流程请假模式

  • 前面的处理完结果了,后面的如果没在自己职责范围内的,只需要知晓,不需要给出新的结果,把前面的结果传递下去即可

且看下面分解案例二

三、案例二

  1. 首先定义好请假请求数据类,里面包含,请假员工名字,请假天数,请假原因,请假状态。
kotlin 复制代码
// 请假请求数据类
data class LeaveRequest(
    val employee: String,
    val days: Int,
    val reason: String,
    var status: String = "待审批"
)
  1. 再提取一个抽象审批处理器,里面含有每一级领导的角色,自己审批请假的天数范围
kotlin 复制代码
// 抽象审批处理器
interface ApprovalHandler {
    val role: String
    val maxDays: Int
    suspend fun process(request: Flow<LeaveRequest>): Flow<LeaveRequest>
}
  1. 实现3个阶级领导的审批处理,在每个实现里面可以看到,如果不在自己审批请假天数范围内,直接把进入的结果传递给下一层。
kotlin 复制代码
// 直属领导审批(1天内)
class TeamLeader : ApprovalHandler {
    override val role = "组长"
    override val maxDays = 1
    override suspend fun process(request: Flow<LeaveRequest>) = request.map {
        println("$role process")

        if(it.days <= maxDays) it.copy(status = "$role 已批准")
        else it
    }
}

// 部门经理审批(3天内)
class DepartmentManager : ApprovalHandler {
    override val role = "部门经理"
    override val maxDays = 3
    override suspend fun process(request: Flow<LeaveRequest>) = request.map {
        println("$role process")

        if(it.days in (TeamLeader().maxDays + 1)..maxDays)
            it.copy(status = "$role 已批准")
        else {
            it
        }
    }
}

// 总经理审批(7天内)
class GeneralManager : ApprovalHandler {
    override val role = "总经理"
    override val maxDays = 7
    override suspend fun process(request: Flow<LeaveRequest>) = request.map {
        println("$role process")

        if(it.days in (DepartmentManager().maxDays + 1)..maxDays)
            it.copy(status = "$role 已批准")
        else it
    }
}

3.定义好责任链处理器,同上面一样,使用 list.fold + flatMapConcat 将处理器串联成链,每个处理器输出作为下一个输入.

kotlin 复制代码
// 责任链处理器
class WXChainHandler2(private val handlers: List<ApprovalHandler>) {

    @OptIn(FlowPreview::class)
    suspend fun process(request: LeaveRequest): Flow<LeaveRequest> {
        return handlers.fold(flowOf(request)) { flow, handler ->
            flow.flatMapConcat { handler.process(flowOf(it)) }
        }.onEach {
            if (it.status == "待审批")
                it.status = "超出权限范围"
        }
    }
}
  1. 实际应用,我们给出四个,分别请假 1天,3天,5天,10天的样例。
kotlin 复制代码
fun main() {
    runBlocking {
        val chain = WXChainHandler2(
            listOf(
                TeamLeader(),
                DepartmentManager(),
                GeneralManager()
            )
        )

        listOf(
            LeaveRequest("张三", 1, "病假"),
            LeaveRequest("李四", 3, "事假"),
            LeaveRequest("王五", 5, "年假"),
            LeaveRequest("赵六", 10, "婚假")
        ).forEach { req ->
            chain.process(req).flowOn(Dispatchers.IO).collect {
                println("${it.employee}申请${it.days}天: ${it.reason} -> ${it.status}")
            }
            println("------------------")
       }
   }
}

5、处理结果:

arduino 复制代码
    组长 process
    部门经理 process
    总经理 process
    张三申请1天: 病假 -> 组长 已批准
    ------------------
    组长 process
    部门经理 process
    总经理 process
    李四申请3天: 事假 -> 部门经理 已批准
    ------------------
    组长 process
    部门经理 process
    总经理 process
    王五申请5天: 年假 -> 总经理 已批准
    ------------------
    组长 process
    部门经理 process
    总经理 process
    赵六申请10天: 婚假 -> 超出权限范围
    ------------------

从日志结果可以看出,我们请假的审批流程,在每一级领导里面都进入去执行了,而真正执行审批的,是请假天数在自己审批范围内的。不在自己审批范围内的,只需要自己知晓,不改变前面的执行的状态,直接依次往下传递。

  • 那么如果我们想要在责任链模式中,前面处理满足了条件才往下传递,满足不了,就执行结束了,后面的都不进入执行了

且看下面分解案例三:

四、案例三

这种案例应用场景有哪些?常见的后端分布式,且是异步处理的某种逻辑,我们需要前面异步接口调用完了,成功后,再去异步拿取前面的结果,拿取完了再执行调用下一步接口。

比如:

  1. 客户端传递参数请求下单接口,下单只返回提交成功的订单号,服务端异步处理下单结果生成支付订单号。
  2. 客户端再次请求,获取下单的支付订单号
  3. 客户端拿到下单的支付订单号去生成支付二维码,等待支付,
  • 这个顺序,只要前面任何一步没有成功,直接不走下一步了。

实现思路:

  1. 先定义好带中断标记的数据类,data:传递数据,shouldContinue:判断是否往下继续执行,为false时候直接终端。
kotlin 复制代码
 data class WXProcessResult<T>(
    val data: T,  //传递数据
    val shouldContinue: Boolean = true  //判断是否往下继续执行
)

2.具体实现三个职责代码如下:

kotlin 复制代码
class WXCreateOrderHandler3 : WXBreakableHandler<String> {
    override suspend fun process(input: String) = flow {
        println("WXCreateOrderHandler3 在执行")
        if (input.contains("order")) {
            emit(WXProcessResult("请求下单成功,拿到 order_sn:103202590"))// 继续传递
        } else {
            emit(WXProcessResult("下单失败,参数错误", false))  // 不满足条件时终止链
        }
    }
}

class WXGetOrderSnHandler3 : WXBreakableHandler<String> {
    override suspend fun process(input: String) = flow {
        println("WXGetOrderSnHandler3 在执行")
        if (input.contains("order_sn")) {
            emit(WXProcessResult("请求拿到支付订单号,pre_sn:1033200")) // 继续传递
        } else {
            emit(WXProcessResult(input, false))  // 不满足条件时终止链
        }
    }
}

class WXPrePay3 : WXBreakableHandler<String> {
    override suspend fun process(input: String) = flow {
        println("WXPrePay3 在执行")
        if (input.contains("pre_sn")) {
            emit(WXProcessResult("请求拿到支付二维码展示数据,供客户端生成二维码 提供给 顾客扫码使用"))
        }else{
            emit(WXProcessResult(input,false))
        }
    }
}
  1. 构建可中断责任链模式处理器:
kotlin 复制代码
class WXChainProcessor3<T>(private val handlers: List<WXBreakableHandler<T>>) {

    fun execute(input: T): Flow<T> {
        return handlers.fold(flowOf(WXProcessResult(input))) { acc, handler ->
            acc.flatMapConcat { result ->
                if (!result.shouldContinue) flowOf(result)
                else handler.process(result.data)
            }
        }.map { it.data }
    }
}
  1. 真正调用实现逻辑:
scss 复制代码
fun main() {
    runBlocking {
        val chain = WXChainProcessor3(
            listOf(
                WXCreateOrderHandler3(),//下单
                WXGetOrderSnHandler3(),  // 获取订单号
                WXPrePay3(),  //支付生成二维码
            )
        )

        listOf("user:order", "user2:dr").forEach { req ->
            chain.execute(req).flowOn(Dispatchers.IO).collect {
                println("最终处理结果: $it")
            }
            println("--------")
        }
    }
}
  1. 查看运行结果:
lua 复制代码
    WXCreateOrderHandler3 在执行
    WXGetOrderSnHandler3 在执行
    WXPrePay3 在执行
    最终处理结果: 请求拿到支付二维码展示数据,供客户端生成二维码 提供给 顾客扫码使用
    --------
    WXCreateOrderHandler3 在执行
    最终处理结果: 下单失败,参数错误
    --------

得出结论:达到了我们想要的效果,我们可以看到,第二个请求下单,下单失败,后面第二步,第三步没有执行了。

  • 那么问题来了:下面这个地方:责任链中3个职责的处理类,要提前创建好,提前占用堆内存,可能我们到第一个后面就中断了,那么第2个,第3个提前创建好把内存占用了不就浪费了吗?
scss 复制代码
val chain = WXChainProcessor3( 
   listOf( 
       WXCreateOrderHandler3(),//下单 
       WXGetOrderSnHandler3(), // 获取订单号 
       WXPrePay3(), //支付生成二维码 
   ) 
)

有没有什么更好的解决办法?

且看下面分解案例四

五、案例四

  1. 按需创建,我们只需要改一下案例三中的第3步:
kotlin 复制代码
// 3. 构建可中断的责任链
class WXChainProcessor3<T>(private val handlers: List<() -> WXBreakableHandler<T>>) {

    fun execute(input: T): Flow<T> {
        return handlers.fold(flowOf(WXProcessResult(input))) { acc, factory ->
            acc.flatMapConcat { result ->
                if (!result.shouldContinue) flowOf(result)
                else {
                    println("正在初始化下一级Handler...")
                    factory().process(result.data)
                }
            }
        }.map { it.data }
    }
}



class WXCreateOrderHandler3 : WXBreakableHandler<String> {

    init {
        println("WXCreateOrderHandler3 创建了")//加入它进行验证
    }

    override suspend fun process(input: String) = flow {
        println("WXCreateOrderHandler3 在执行")
        if (input.contains("order")) {
            emit(WXProcessResult("请求下单成功,拿到 order_sn:103202590"))// 继续传递
        } else {
            emit(WXProcessResult("下单失败,参数错误", false))  // 不满足条件时终止链
        }
    }
}

class WXGetOrderSnHandler3 : WXBreakableHandler<String> {

    init {
        println("WXGetOrderSnHandler3 创建了")//加入它进行验证
    }

    override suspend fun process(input: String) = flow {
        println("WXGetOrderSnHandler3 在执行")
        if (input.contains("order_sn")) {
            emit(WXProcessResult("请求拿到支付订单号,pre_sn:1033200")) // 继续传递
        } else {
            emit(WXProcessResult(input, false))  // 不满足条件时终止链
        }
    }
}

class WXPrePay3 : WXBreakableHandler<String> {

    init {
        println("WXPrePay3 创建了")//加入它进行验证
    }

    override suspend fun process(input: String) = flow {
        println("WXPrePay3 在执行")
        if (input.contains("pre_sn")) {
            emit(WXProcessResult("请求拿到支付二维码展示数据,供客户端生成二维码 提供给 顾客扫码使用"))
        }else{
            emit(WXProcessResult(input,false))
        }
    }
}
  1. 具体实现方更改成它:
scss 复制代码
val chain = WXChainProcessor3(
    listOf(
        { WXCreateOrderHandler3() },//下单
        { WXGetOrderSnHandler3() },  // 获取订单号
        { WXPrePay3() },  //支付生成二维码
    )
)
  1. 验证输出结果:
markdown 复制代码
正在初始化下一级Handler...
WXCreateOrderHandler3 创建了
WXCreateOrderHandler3 在执行
正在初始化下一级Handler...
WXGetOrderSnHandler3 创建了
WXGetOrderSnHandler3 在执行
正在初始化下一级Handler...
WXPrePay3 创建了
WXPrePay3 在执行
最终处理结果: 请求拿到支付二维码展示数据,供客户端生成二维码 提供给 顾客扫码使用
--------
正在初始化下一级Handler...
WXCreateOrderHandler3 创建了
WXCreateOrderHandler3 在执行
最终处理结果: 下单失败,参数错误
--------

从输出结果我们可以看到已经达到了我们想要的效果。

六、总结

本文重点介绍了Kotlin + Flow 实现责任链模式的4种案例

  1. 第一种,每个责任类都输出不同的结果
  2. 第二种,在自己职责范围出来给结果,之外不改变结果,但是还是要执行后面的处理,只是不改变结果
  3. 第三种,可以在不满足条件下直接中断,不执行后面的责任职责
  4. 第四种,在第3种基础上,懒加载方式,只有用到了才开始创建执行当前职责的类。没有用到不先提前预创建好。

感谢阅读:

欢迎用你发财的小手 关注,点赞、收藏

这里你会学到不一样的东西

相关推荐
alexhilton1 小时前
务实的模块化:连接模块(wiring modules)的妙用
android·kotlin·android jetpack
予枫的编程笔记1 小时前
Redis 核心数据结构深度解密:从基础命令到源码架构
java·数据结构·数据库·redis·缓存·架构
ji_shuke2 小时前
opencv-mobile 和 ncnn-android 环境配置
android·前端·javascript·人工智能·opencv
meilididiao2 小时前
低代码应用-动态指标跟踪评测系统
低代码·架构
秋4273 小时前
防火墙基本介绍与使用
linux·网络协议·安全·网络安全·架构·系统安全
sunnyday04264 小时前
Spring Boot 项目中使用 Dynamic Datasource 实现多数据源管理
android·spring boot·后端
幽络源小助理5 小时前
下载安装AndroidStudio配置Gradle运行第一个kotlin程序
android·开发语言·kotlin
inBuilder低代码平台5 小时前
浅谈安卓Webview从初级到高级应用
android·java·webview
豌豆学姐5 小时前
Sora2 短剧视频创作中如何保持人物一致性?角色创建接口教程
android·java·aigc·php·音视频·uniapp
白熊小北极5 小时前
Android Jetpack Compose折叠屏感知与适配
android