面向"轨道"编程

这两天看了一份关于MonadPPT,将使用Monad比喻成了面向轨道编程,觉得写的挺好的,周末特意写篇文章记录一下。首先我们看一段代码,这段代码模拟了一个处理request的业务逻辑:

// 模拟处理业务
fun executeRequest(request: Request) : String {
    // 校验身份
    validateRequest(request)
    // 处理业务
    dealBiz(request)
    // 存储数据
    saveToDB(request)
    // 发送消息
    sendMessage(request)
    return "success"
}

看起来这些代码很普通,但是在平时的业务代码里运行起来通常会遇到各种异常,也就难免我们需要很多的数据判断来避免这些异常破坏我们的逻辑了。所以实际的代码很可能会写成这样:

// 模拟处理业务
fun executeRequest(request: Request) : String {
    // 校验身份
    val isValidate = validateRequest(request)
    if( isValidate ) {
        return "Request is not valid"
    }
    // 处理业务
   dealBiz(request)
    try {
        // 存储数据
        saveToDB(request)
    } catch (exception:Exception) {
        return "Occur error when save results to DB"
    }
    // 发送消息
    val isSendSuccess = sendMessage(request)
    if (isSendSuccess == false) {
         return "message send unsuccessfully."
    }
    return "success"
}

executeRequest()函数在根据业务组装自己的控制流,实际的业务代码中有很多这种判断,为了避免执行到不应该被执行到的代码。但这种判断越来越多,代码就越难维护了。有一个笑话称这种代码是“上帝代码”,除了自己和上帝没有人能看懂,过了一段时间之后,只有上帝能看懂了。

那么如何让这些代码变得简单易读呢?先看一个例子,假设有两个函数,他们的作用如下:

  • 牛 --> 牛肉
  • 牛肉 --> 牛肉干

可以把函数的处理想象成铁轨,就像下面这样:

牛 --> 牛肉
牛肉 --> 牛肉干

然后我们再把这个两个函数合并一下:

牛 --> 牛肉 --> 牛肉干

函数总是的执行总是两种情况,成功或者失败。这个函数执行的过程可能不会这么顺利,也许制作牛肉的过程就会有发生异常,也许制作牛肉干的过程会失败。所以可以定义一个ResultMonads容器,接收返回值,Result定义的时候泛型可以指定两个类型一个是正常返回类型(Result.Success),另外一个携带是Exception的返回类型(Result.Failure)。如果函数正常执行,就是用success()函数处理,如果中间有一个失败了,则是用failure()函数处理。

val result : Result<Boolean,Exception> = Result.of( 1 + 1 = 2 )

result.success {
    // 处理正常业务逻辑
}

result.failure {
    // 处理错误
}

这个时候,函数的执行就像在两条轨道上一样,一旦函数出现错误,执行函数的“火车”就可以驶向专门处理错误的轨道上一样:

那么我们再回到之前的例子,利用Result我们可以先把业务函数(即validateRequest(),dealBiz()等等)全部设计成返回Result,组装这些业务函数的方法就可以这样写了:

fun executeRequest(request: Request) : String {
  val result = Result
            .of(request)
            .flatMap { validateRequest(request) }
            .flatMap { dealBiz(request) }
            .flatMap { saveToDB(request) }
            .flatMap { sendMessage(request) }
  result.fold(
            success = { return "success" },
            failure = { return it.message }
  )
}

我们把异常定义在业务函数中,直接利用Exception的message抛出,交由上层统一抛出。代码看着清晰了很多。特别是每个flapMap代码块中都在处理各自的业务,然后也可以将结果传递给下一个代码块。这里解释一下Result中的map函数和flatMap函数:

  • map() : 将函数的结果计算完毕之后,转换成一个新的Result对象返回。
  • flatMap() : 将函数的结果计算完毕之后直接返回,比如结果是true,那么直接返回一个布尔类型(Boolean)。

以上两个函数如果遇到带有异常的Result,会直接将这个异常的``Result返回。而下面两个函数是专门用来处理失败的Result

  • mapError() : 将函数新的异常捕获之后,转换成一个新的Result带有新异常的对象返回。
  • flatMapError() : 将函数的结果获得的新异常返回。

同样,如果遇到成功的Result,那么函数会直接将这个成功的Result返回。之前也看过很多关于Monads的文章,虽然看着很厉害的样子,但是一直不知道为什么要去使用Monads,其实就是为了让代码的业务逻辑和控制能分的更加清楚一些。之前读过一篇文章说,编程范式的本质是有效地分离Logic,ControlData,即:

  • Logic : 就是一般的业务代码,类似上面代码中的dealBiz(),sendMessage()等等
  • Control : 对业务逻辑的流程控制,比如遍历数据、查找数据、多线程、并发、异步等等
  • Data :函数和程序之间传递的这部分信息

所以面向“轨道”编程,就是设计了Result这样一套模型来分离了LogicControl

Ps. 文中使用的是一个根据面向轨道设计Kotlin库:https://github.com/kittinunf/Result

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

推荐阅读更多精彩内容