Kotlin(十四)设计模式-行为型模式(责任链模式)

用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十分灵活。

©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 197,273评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,905评论 2 374
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 144,281评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,817评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,690评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,491评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,886评论 3 388
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,513评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,810评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,839评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,642评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,455评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,901评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,091评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,381评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,895评论 2 343
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,104评论 2 338

推荐阅读更多精彩内容