欢迎重视微信大众号:FSA全栈举动
- Kotlin – 改进工厂形式
- Kotlin – 改进构建者形式
- Kotlin – 改进观察者形式
- Kotlin – 改进策略形式
- Kotlin – 改进迭代器形式
- Kotlin – 改进职责链形式
- Kotlin – 改进装修者形式
一、前语
- 职责链形式
- 效果:避免恳求的发送者和接纳者之间的耦合关系,将这个目标连成一条链,并沿着这条链传递该恳求,直到有一个目标处理它为止。
- 举例:OKHttp 的拦截器、Servlet 中的 FilterChain
二、运用职责链形式
- 比如:学生会经费请求
- 要点:1 个恳求会在 n 个处理器组成的处理器链上传递
以学生会经费请求会例,学生会会有一些日常开支以及活动开支,需求向学院的学生会基金请求经费,假如金额在 100 元之内,由分部长批阅;假如金额在 100 到 500 元之间,由会长批阅;假如金额在 500 到 1000 元之间,由学院辅导员批阅;而假如金额超过 1000 元,则默许打回请求。像这种需求一层层往后传递恳求的状况,十分适合选用职责链形式来规划程序:
/**
* 经费请求事情
*
* @author GitLqr
*/
data class ApplyEvent(val money: Int, val title: String)
/**
* 经费批阅处理器
*
* @author GitLqr
*/
interface ApplyHandler {
val successor: ApplyHandler?
fun handleEvent(event: ApplyEvent)
}
留意:职责链形式需求将处理器目标连成一条链,最简单粗暴的办法便是让前驱处理器持有后继处理器
successor
接着,依据事例需求,编写各个人物对应的处理器类:
/**
* 部长
*
* @author GitLqr
*/
class GroupLeader(override val successor: ApplyHandler?) : ApplyHandler {
override fun handleEvent(event: ApplyEvent) {
when {
event.money <= 100 -> println("Group Leader handled application: ${event.title}.")
successor != null -> successor.handleEvent(event)
else -> println("Group Leader: This application cannot be handled.")
}
}
}
/**
* 会长
*
* @author GitLqr
*/
class President(override val successor: ApplyHandler?) : ApplyHandler {
override fun handleEvent(event: ApplyEvent) {
when {
event.money <= 500 -> println("President handled application: ${event.title}.")
successor != null -> successor.handleEvent(event)
else -> println("President: This application cannot be handled.")
}
}
}
/**
* 学院
*
* @author GitLqr
*/
class College(override val successor: ApplyHandler?) : ApplyHandler {
override fun handleEvent(event: ApplyEvent) {
when {
event.money <= 1000 -> println("College handled application: ${event.title}.")
successor != null -> successor.handleEvent(event)
else -> println("College: This application cannot be handled.")
}
}
}
最终,创立各个人物处理器实例,并按顺序组成一条链,由链头开端接纳、转发需求被处理的经费请求事情:
// 运用
// val college = College(null)
// val president = President(college)
// val groupLeader = GroupLeader(president)
val groupLeader = GroupLeader(President(College(null)))
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 meeting of the college")) // 学院年会
// 输出
Group Leader handled application: buy a pen.
President handled application: team building.
College handled application: hold a debate match.
College: This application cannot be handled.
从输出成果能够看到,经费请求事情会在处理器链上传递,直到被一个合适的处理器处理并停止。
留意:这话是针对当前事例说的,职责链形式没有硬性要求一个恳求只能被一个处理器处理,你能够在前面的处理器中对恳求进行加工,提取数据等等操作,而且能够挑选是否放行,交由后边的处理器持续处理,这需求依据实际状况,灵活应变。
三、改进职责链形式
- 比如:学生会经费请求
- 要点:偏函数
Partial Function
在对上述事例进行改进之前,咱们先来了解一下偏函数是什么,在不同的编程语言中,对偏函数的了解还不相同,在 Python 中,偏函数是运用 functools.partial
把一个函数的某些参数给固定住(也便是设置默许值),回来一个新的函数,调用这个新函数会更简单。而在 Scala 中,偏函数是运用 PartialFunction
构建一个仅仅处理输入参数的部分分支的函数,换句话说,便是带有判别条件的函数,只有满足条件的参数,才会被函数处理。
以上结论来自以下两篇文章:
- 廖雪峰官方网站上的 Python 《偏函数》www.liaoxuefeng.com/wiki/101695…
- 数据工匠记(作者:匠人李)的 Scala 《偏函数(Partial Function)》www.lllpan.top/article/77
题外话:对 Scala 偏函数有爱好的能够看一下上面的文章,写的很通透。
回过头来,职责链形式的中心机理是,整个链条上的每个处理环节都有对其输入的校验标准,当输入的参数处于某个职责链节的有用接纳规模之内,该环节才能对其做出正常的处理操作。那么,咱们是不是能够把链条上的每个处理环节看做是一个个的偏函数呢?是的,不过 Kotlin 中并没有内置偏函数 API,好在有一个第三方 Kotlin 函数库【funKTionale】,其间的 partialfunctions.kt
就有 Scala 中偏函数的类似完成:
// https://github.com/MarioAriasC/funKTionale/blob/master/funktionale-utils/src/main/kotlin/org/funktionale/utils/partialfunctions.kt
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 (definetAt(p1)) {
return f(p1)
} else {
throw IllegalArgumentException("Value: ($p1) isn't supported by this function")
}
}
fun isDefinedAt(p1: P1) = definetAt(p1)
}
这个 PartialFunction
类第一眼看上去感觉好复杂,分红如下几步,便利了解:
-
PartialFunction
继承自一个函数类型(P1) -> R
,编译器会强制要求完成invoke()
办法,这意味着PartialFunction
实例目标能够像调用函数那样运用。 - 结构参数 1
definetAt: (P1) -> Boolean
用于判别P1
参数是否满足被处理的条件。 - 结构参数 2
f: (P1) -> R
用于处理P1
参数并回来R
类型值。 - 成员办法
invoke
中,当 P1 满足条件时,则将 P1 交给 结构参数 2f: (P1) -> R
处理;不然抛出反常。 - 成员办法
isDefinedAt
仅仅结构参数 1definetAt
的复制。
所以,用一句话概括 PartialFunction
实例目标,便是一个带有判别条件的”函数”,只有满足条件的参数,才会被”函数”处理。现在咱们用一个个 PartialFunction
实例来代替处理器是彻底没问题的,问题是怎样把它们链接起来呢?【funKTionale】中还为 PartialFunction
扩展了一个 orElse
函数,这便是把偏函数组合起来的要害:
// https://github.com/MarioAriasC/funKTionale/blob/master/funktionale-utils/src/main/kotlin/org/funktionale/utils/partialfunctions.kt
infix fun <P1, R> PartialFunction<P1, R>.orElse(that: PartialFunction<P1, R>): PartialFunction<P1, R> {
return PartialFunction({ this.isDefinedAt(it) || that.isDefinedAt(it) }) {
when {
this.isDefinedAt(it) -> this(it)
that.isDefinedAt(it) -> that(it)
else -> throw IllegalArgumentException("function not definet for parameter ($it)")
}
}
}
相同,也分红如下几步,便利了解:
-
orElse
是PartialFunction
的扩展函数,故内部能够运用this
获取本来的PartialFunction
实例(也便是receiver
)。 -
orElse
只接纳一个PartialFunction
类型参数that
,而且回来一个PartialFunction
类型实例,故orElse
能够嵌套调用。 -
orElse
回来值是一个运用了两个PartialFunction
实例目标 (即this
和that
)组合出来的一个新的PartialFunction
实例目标, -
orElse
回来值的目的是,只要本来的this
和that
中有一个条件成立,那么就让条件成立的那个来处理参数 P1 ,不然抛出反常。其实,这个that
就适当于是职责链形式中的successor
。 -
orElse
运用infix
润饰,故支持中缀表达式写法。
留意:你或许一时看不懂
PartialFunction({ xxx }){ yyy }
这个奇怪的语法,其实很简单,在创立一个PartialFunction
实例时,能够传入两个 Lambda 表达式,所以正常写法应该是这样的PartialFunction({ xxx }, { yyy })
,不过,在 Kotlin 中,当 Lambda 表达式作为最终一个参数传入时,能够写到函数外部,所以就出现了PartialFunction({ xxx }){ yyy }
这种写法。
好了,现在用 PartialFunction
来改进本来的职责链形式代码:
/**
* 运用自运行Lambda来构建一个个 PartialFunction 实例:部长、会长、学院
*
* @author GitLqr
*/
val groupLeader = {
val definetAt: (ApplyEvent) -> Boolean = { it.money <= 200 }
val handler: (ApplyEvent) -> Unit = { println("Group Leader handled application: ${it.title}.") }
PartialFunction(definetAt, handler)
}()
val president = {
val definetAt: (ApplyEvent) -> Boolean = { it.money <= 500 }
val handler: (ApplyEvent) -> Unit = { println("President handled application: ${it.title}.") }
PartialFunction(definetAt, handler)
}()
val college = {
val definetAt: (ApplyEvent) -> Boolean = { true }
val handler: (ApplyEvent) -> Unit = {
when {
it.money <= 1000 -> println("College handled application: ${it.title}.")
else -> println("College: This application is refused.")
}
}
PartialFunction(definetAt, handler)
}()
留意:自运行 Lambda 适当于是 js 中的当即执行函数。
接下来便是用 orElse
将一个个 PartialFunction
实例链接起来:
// 运用
// val applyChain = groupLeader.orElse(president.orElse(college))
val applyChain = groupLeader orElse president orElse college // 中缀表达式
applyChain(ApplyEvent(10, "buy a pen")) // 买只钢笔
applyChain(ApplyEvent(200, "team building")) // 团建
applyChain(ApplyEvent(600, "hold a debate match")) // 举办辩论赛
applyChain(ApplyEvent(1200, "annual meeting of the college")) // 学院年会
// 输出
Group Leader handled application: buy a pen.
Group Leader handled application: team building.
College handled application: hold a debate match.
College: This application is refused.
运用 PartialFunction
之后,不只能够不幅度减少代码量,结合 orElse
能取得更好的语法表达。以上,便是运用偏函数改进职责链形式的全部内容了。为了加深对偏函数的了解,这里引用数据工匠记的 Scala 《偏函数(Partial Function)》原文中的话:
为什么要用偏函数呢?以我个人愚见,还是一个重用粒度的问题。函数式的编程思维是以一种“演绎法”而非“概括法”去寻求处理空间。也便是说,它并不是要去概括问题然后分解问题并处理问题,而是看透问题本质,定义最原初的操作和组合规则,面临问题时,能够通过组合各种函数去处理问题,这也正是“组合子(combinator)”的意义。偏函数则更进一步,将函数求解空间中各个分支也分离出来,形成能够被组合的偏函数。
假如文章对您有所帮助, 请不惜点击重视一下我的微信大众号:FSA全栈举动, 这将是对我最大的激励. 大众号不只有Android技能, 还有iOS, Python等文章, 或许有你想要了解的技能知识点哦~