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种基础上,懒加载方式,只有用到了才开始创建执行当前职责的类。没有用到不先提前预创建好。

感谢阅读:

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

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

相关推荐
水沝淼燚1 分钟前
kmp的实际使用1,开发android项目和native转kotlin开发
android
CYRUS_STUDIO15 分钟前
破解 VMP+OLLVM 混淆:通过 Hook jstring 快速定位加密算法入口
android·算法·逆向
Renounce25 分钟前
【Android】四大组件Service
android
没有了遇见27 分钟前
Activity 启动模式总结
android
wangjialelele44 分钟前
二叉树基本学习
android
掘金-我是哪吒2 小时前
分布式微服务系统架构第152集:JavaPlus技术文档平台日更
分布式·微服务·云原生·架构·系统架构
雨白4 小时前
Android 音视频播放:MediaPlayer 与 VideoView
android
Harry技术4 小时前
Fragment 和 AppCompatActivity 两个核心组件设计的目的和使用场景对比
android·android studio
Renounce4 小时前
【Android】四大组件Activity
android
风铃喵游4 小时前
平地起高楼: 环境搭建
前端·架构