用Kotlin偏函数-实现责任链模式
典型的例子是Servlet中Filter和FilterChain接口,做过web开发的应该比较熟悉。
责任链的目的:避免请求的发送者和接受者之间的耦合关系,将这个对象连成一条链,并沿着这个链传递该请求,直到有一个对象处理它为止。
前面opengl的文章,做各种效果美颜等叠加用的就是责任链。
举个例子:
大学学生会管理学生基金,用于各种活动和组织。发生一笔支出时,如果金额100以内,可以由各个分部长审批;超过100,需要会长同意;金额较大超过500需要辅导员同意。此外,有一个限制,超过1000 默认打回申请。
实现一个普通的责任链
data class ApplyEvent(val money: Int, val title: String)
interface ApplyHandler {
val successor: ApplyHandler?
fun handleEvent(event: ApplyEvent)
}
class GroupLeader(override val successor: ApplyHandler?) : ApplyHandler {
override fun handleEvent(event: ApplyEvent) {
when {
event.money <= 100 ->
println("Group leader handle application:${event.title}")
else -> when (successor) {
is ApplyHandler -> successor.handleEvent(event)
else -> println("Group Leader:This application cannot be handle.")
}
}
}
}
class Pressient(override val successor: ApplyHandler?) : ApplyHandler {
override fun handleEvent(event: ApplyEvent) {
when {
event.money <= 500 ->
println("Group leader handle application:${event.title}")
else -> when (successor) {
is ApplyHandler -> successor.handleEvent(event)
else -> println("Group Leader:This application cannot be handle.")
}
}
}
}
class College(override val successor: ApplyHandler?) : ApplyHandler {
override fun handleEvent(event: ApplyEvent) {
when {
event.money > 1_000 ->
println("College:This application is refused.")
else -> println("College handled application :${event.title}")
}
}
}
fun main() {
val college = College(null)
val president = Pressient(college)
val groupLeader = GroupLeader(president)
groupLeader.handleEvent(ApplyEvent(10,"buy a pen"))
groupLeader.handleEvent(ApplyEvent(200,"team building"))
groupLeader.handleEvent(ApplyEvent(600,"hold a debate match"))
groupLeader.handleEvent(ApplyEvent(1200,"annual metting of the college"))
}
每个链条的每个环节都有对它输入参数的校验标准,输入参数在某个责任链环节的有效范围内,该环节做出正常处理,我们还有专业属于描述,就是 “偏函数”
实现偏函数类型:PartialFunction
偏函数:数学概念,定义域X中可能存在某些值,在值域Y中没有对应的值
和普通函数比较 (int) -> Unit ,可以接受任何Int值。在偏函数,指定的参数并不接受任何类型。
// 偏函数
fun mustGreaterThan5(x:Int):Boolean{
if(x > 5) return true
else throw Exception("x muster greater than 5")
}
Kotlin 的标准库,并没有支持PartialFunction。然而Scala里面有PartialFunction类型。Ktolin 第三方库(如funKTionale)已经实现了这个功能。
PartialFunction
class PartialFunction<in P1, out R>
(private val definetAt: (P1) -> Boolean, private val f: (P1) -> R) : (P1) -> R {
override fun invoke(p1: P1): R {
if (isDefinetAt(p1)) {
return f(p1)
} else {
throw IllegalArgumentException("Value:$p1 is not support by this function")
}
}
fun isDefinetAt(p1: P1) = definetAt(p1)
}
- 声明对象的时候接受两个构造参数,其中definetAt 是校验函数,f是处理函数
- 当PartialFunction类对象执行invoke方法,definetAt会对输出参数p1 进行有效行校验
- 如果校验结果通过,则执行f函数,同时将p1作为参数传递给它;反过来抛出异常
我们还需要解决一个问题就是,如何在链条请求中进行传递
orElse 扩展,为了可以用中缀表达式,链式调用,添加infix
infix fun <P1, R> PartialFunction<in P1, out R>.orElse(that: PartialFunction<P1, R>): PartialFunction<P1, R> {
return PartialFunction({ this.isDefinetAt(it) || that.isDefinetAt(it) }) {
when {
this.isDefinetAt(it) -> this(it)
else -> that(it)
}
}
}
isDefinetAt 没有特殊的地方就是单纯的拷贝definetAt 方法目的是可以在orElse 中使用
orElse 方法可以传入PartialFunction对象that,也就是责任链后继,如果isDefinetAt 为false 调用that继续申请处理。
orElse 方式实现责任链
利用设计好的PartialFunction类以及扩展orElse方法实现
val groupLeader = {
val definetAt: (ApplyEvent) -> Boolean = { it.money <= 200 }
val handler: (ApplyEvent) -> Unit = { println("Group leader handle application:${it.title}") }
PartialFunction(definetAt, handler)
}()
val president = {
val definetAt: (ApplyEvent) -> Boolean = { it.money <= 500 }
val handler: (ApplyEvent) -> Unit = {
println("Group leader handle application:${it.title}")
}
PartialFunction(definetAt, handler)
}()
val college = {
val definetAt: (ApplyEvent) -> Boolean = { true }
val handler: (ApplyEvent) -> Unit = {
when {
it.money > 1_000 ->
println("College:This application is refused.")
else -> println("College handled application :${it.title}")
}
}
PartialFunction(definetAt, handler)
}()
val applyChain = groupLeader orElse president orElse college
fun main() {
applyChain(ApplyEvent(100,"hold a debate match"))
applyChain(ApplyEvent(600,"hold a debate match"))
applyChain(ApplyEvent(1200,"hold a debate match"))
}
val applyChain = groupLeader orElse president orElse college
中缀表达式,kotlin十分灵活。