
责任链模式在开发中,用得还算是比较广泛的,掌握它的使用,对于架构或者写框架都有很大帮助
一、前言
责任链模式通过构建链式处理结构实现请求的动态传递,其核心优势在于降低系统耦合度与提升扩展性, 它主要优点是:
- 请求发送者与处理者解耦
- 动态扩展能力强
- 职责分离明确
- 在Android中,用到责任链的主要框架有哪些:
- ViewGroup的事件分发责任链
- OkHttp的拦截器责任链
- Glide的资源加载责任链
- 但是它们涉及到的责任链都是传统责任链模式写法。
我们知道Flow也可以链式调用,那么采用Flow实现责任链模式相比于传统方式实现责任链模式有哪些优缺点呢?
Flow实现责任链与传统责任链对比:
特性 | Flow实现方案 | 传统责任链模式 |
---|---|---|
异步支持 | ✅ 内置协程异步处理 | ❌ 需手动实现线程池 |
背压管理 | ✅ 自动流速控制 | ❌ 需额外实现 |
链式组合 | ✅ 函数式操作符连接 | ✅ 对象指针连接 |
错误处理 | ✅ 统一catch捕获 | ❌ 需逐级传递异常 |
本文重点介绍 :
Kotlin + Flow 实现责任链模式的4种案例,具体看它怎么实现
二、案例一
我们有个需求,是3个请求,需要分三步走:
- 第一步输入账号登录,如果没有认证,认证完再进行第二步
- 在第二步进行各种校验,校验成功后走第三步,校验错误直接抛异常
- 第三步进行一些记录。
具体代码实现:
- 定义好处理器接口
kotlin
interface WXChainHandler<T> {
suspend fun process(input: T): Flow<T>
}
- 实现三个步骤的各自职责
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
支持异步操作。
统一使用Flow
的catch
操作符统一捕获全链异常
背压支持 :通过使用 Flow
的 flatMapConcat
自动处理生产者-消费者速度差异。
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 在处理
最终结果: 处理异常: 输入长度不足
--------
从上面日志里面我们可以得出结论:
- 里面每一个都在处理,处理完了的结果给到下一个再来处理。
- 这里的每一个处理对于客户端来说可能是一个耗时操作,可以通过配置在异步里面。
- 里面每一个都在处理,处理完的结果都是新的结果。最终给出。
那么责任链模式中还有一种最经典的,IO流程请假模式
:
前面的处理完结果了,后面的如果没在自己职责范围内的,只需要知晓,不需要给出新的结果,把前面的结果传递下去即可
。
且看下面分解案例二
三、案例二
- 首先定义好请假请求数据类,里面包含,请假员工名字,请假天数,请假原因,请假状态。
kotlin
// 请假请求数据类
data class LeaveRequest(
val employee: String,
val days: Int,
val reason: String,
var status: String = "待审批"
)
- 再提取一个抽象审批处理器,里面含有每一级领导的角色,自己审批请假的天数范围
kotlin
// 抽象审批处理器
interface ApprovalHandler {
val role: String
val maxDays: Int
suspend fun process(request: Flow<LeaveRequest>): Flow<LeaveRequest>
}
- 实现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天,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天: 婚假 -> 超出权限范围
------------------
从日志结果可以看出,我们请假的审批流程,在每一级领导里面都进入去执行了,而真正执行审批的,是请假天数在自己审批范围内的。不在自己审批范围内的,只需要自己知晓,不改变前面的执行的状态,直接依次往下传递。
那么如果我们想要在责任链模式中,前面处理满足了条件才往下传递,满足不了,就执行结束了,后面的都不进入执行了
。
且看下面分解案例三:
四、案例三
这种案例应用场景有哪些?常见的后端分布式,且是异步处理的某种逻辑,我们需要前面异步接口调用完了,成功后,再去异步拿取前面的结果,拿取完了再执行调用下一步接口。
比如:
- 客户端传递参数请求下单接口,下单只返回提交成功的订单号,服务端异步处理下单结果生成支付订单号。
- 客户端再次请求,获取下单的支付订单号
- 客户端拿到下单的支付订单号去生成支付二维码,等待支付,
- 这个顺序,只要前面任何一步没有成功,直接不走下一步了。
实现思路:
- 先定义好带中断标记的数据类,
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))
}
}
}
- 构建可中断责任链模式处理器:
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 }
}
}
- 真正调用实现逻辑:
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("--------")
}
}
}
- 查看运行结果:
lua
WXCreateOrderHandler3 在执行
WXGetOrderSnHandler3 在执行
WXPrePay3 在执行
最终处理结果: 请求拿到支付二维码展示数据,供客户端生成二维码 提供给 顾客扫码使用
--------
WXCreateOrderHandler3 在执行
最终处理结果: 下单失败,参数错误
--------
得出结论:达到了我们想要的效果,我们可以看到,第二个请求下单,下单失败,后面第二步,第三步没有执行了。
- 那么问题来了:下面这个地方:责任链中3个职责的处理类,要提前创建好,提前占用堆内存,可能我们到第一个后面就中断了,那么第2个,第3个提前创建好把内存占用了不就浪费了吗?
scss
val chain = WXChainProcessor3(
listOf(
WXCreateOrderHandler3(),//下单
WXGetOrderSnHandler3(), // 获取订单号
WXPrePay3(), //支付生成二维码
)
)
有没有什么更好的解决办法?
且看下面分解案例四
五、案例四
- 按需创建,我们只需要改一下案例三中的第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))
}
}
}
- 具体实现方更改成它:
scss
val chain = WXChainProcessor3(
listOf(
{ WXCreateOrderHandler3() },//下单
{ WXGetOrderSnHandler3() }, // 获取订单号
{ WXPrePay3() }, //支付生成二维码
)
)
- 验证输出结果:
markdown
正在初始化下一级Handler...
WXCreateOrderHandler3 创建了
WXCreateOrderHandler3 在执行
正在初始化下一级Handler...
WXGetOrderSnHandler3 创建了
WXGetOrderSnHandler3 在执行
正在初始化下一级Handler...
WXPrePay3 创建了
WXPrePay3 在执行
最终处理结果: 请求拿到支付二维码展示数据,供客户端生成二维码 提供给 顾客扫码使用
--------
正在初始化下一级Handler...
WXCreateOrderHandler3 创建了
WXCreateOrderHandler3 在执行
最终处理结果: 下单失败,参数错误
--------
从输出结果我们可以看到已经达到了我们想要的效果。
六、总结
本文重点介绍了Kotlin + Flow 实现责任链模式的4种案例
- 第一种,每个责任类都输出不同的结果
- 第二种,在自己职责范围出来给结果,之外不改变结果,但是还是要执行后面的处理,只是不改变结果
- 第三种,可以在不满足条件下直接中断,不执行后面的责任职责
- 第四种,在第3种基础上,懒加载方式,只有用到了才开始创建执行当前职责的类。没有用到不先提前预创建好。