开发中如何利用函数式编程写出简洁优美的业务代码?

2022-08-1123:14:09后端程序开发Comments987 views字数 2725阅读模式

日常的开发中,一定有一些问题常常困扰着你。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27023.html

比如接口返回的字段不符合预期:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27023.html

const data = await fetchData()
const foo = data.a.b.c.d
// Uncaught TypeError: Cannot read property 'c' of undefined

比如代码中存在未处理的边缘条件,某些特定情况下会触发:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27023.html

const list = [{ name: 'tom', age: 18 }]
const { name } = list.find(person => person.age === 20)
// Uncaught TypeError: Cannot destructure property `name` of 'undefined' or 'null'

为了避免这种尴尬的情况,我们在代码中一般会使用丑陋的“防御式编程”:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27023.html

const data = await fetchData()
let foo
if (data && data.a && data.a.b && data.a.b.c) {
    foo = data.a.b.c.d
}

const list = [{ name: 'tom', age: 18 }]
const person = list.find(person => person.age === 20)
let name
if (person && person.name) {
    name = person.name
}

看到这样的代码,不知道你的感觉怎么样,反正我是被丑到了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27023.html

而 Maybe 正是帮助你从 null 和 undefined 的茫茫苦海中脱离的好东西。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27023.html


什么是 Maybe

Maybe 是函数式编程中的一个概念,是一种常用的函子(functor),通常用来处理函数式编程中可能存在的空值问题。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27023.html

讲理论没什么意思,我们直接来看怎么使用。当然,不同的 Maybe 实现,接口可能不太一样,这里我们使用 folktale 提供的 Maybe 对象:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27023.html

const Maybe = require('folktale/maybe')

一个 Maybe 对象,只可能存在两种状态:Just(value) 和 Nothing文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27023.html

const Maybe = require('folktale/maybe')

// Just 可以直接 .get()
Maybe.Just(42).get()
//=> 42

// Nothing.get() 会报错
Maybe.Nothing().get()
//=> TypeError: Can't extract the value of a Nothing.

// 所以为了安全起见,一般使用 getOrElse() 来处理 Nothing
Maybe.Just(42).getOrElse(666)
//=> 42
Maybe.Nothing().getOrElse(666)
//=> 666

// 此外,.map() 还可以自动区分 Just 和 Nothing
Maybe.Just(42).map(x => x + 1)
//=> Maybe.Just(43)
Maybe.Nothing().map(x => x + 1)
//=> Maybe.Nothing()

一个 Maybe 对象内部是不确定的,打个比方就是 “薛定谔的猫”,处于叠加态。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27023.html

开发中如何利用函数式编程写出简洁优美的业务代码?文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27023.html

Maybe 这个“盒子”在拆开之前,在里面的东西既是 Just(value),也是 Nothing,只有在打开盒子的那一刻,才确定最终状态。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27023.html

const Maybe = require('folktale/maybe')

const foo = Maybe.fromNullable(Math.random() >= 0.5 ? 'bar' : undefined)
//=> 50%概率为 Maybe.Just('bar'),50%概率为 Maybe.Nothing()

const unsafe_result = foo.get()
// 有50%的概率会报错:
// TypeError: Can't extract the value of a Nothing.

// 为了安全起见,我们使用 getOrElse()
const result = foo.getOrElse('nothing!!!')
// 50%:'bar',50%:'nothing!!!'

实战中如何使用

Maybe 非常适合处理代码中的不确定性,因为它把不确定性的处理,推迟到最后的取值处,而不是在代码中间穿插,例如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27023.html

const Maybe = require('folktale/maybe')
const list = [{ name: 'tom', age: 18 }]

// 丑陋的防御式代码
const person = list.find(person => person.name === 'stark')
let ageText = 'No Age'
if (person && person.age !== undefined) {
    ageText = `User Age: ${person.age}`
}

// Maybe
const ageText = Maybe.fromNullable(
        list.find(person => person.name === 'stark')
    )
    .map(person => `User Age: ${person.age}`)
    .getOrElse('No Age')

我们还可以用 Maybe 来实现一个简单地 safe get:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27023.html

const Maybe = require('folktale/maybe')
function get(object, ...keys) {
    if (keys.length === 0) {
        return Maybe.Just(object)
    } else if (object[keys[0]]) {
        return get(object[keys[0]], ...keys.slice(1))
    } else {
        return Maybe.Nothing()
    }
}

const foo = { a: { b: { c: { d: 42 } } } }
const bar = {}

get(foo 'a', 'b', 'c', 'd').getOrElse(0)
//=> 42

get(bar 'a', 'b', 'c', 'd').getOrElse(0)
//=> 0

使用 Maybe 的 safe get,用在接口调用处会非常爽:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27023.html

const data = await fetchData()
const firstUserName = get(data, 'list', 0, 'name').getOrElse('no user found')

如果使用防御式编程,代码会变成下面这样恶心:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27023.html

const data = await fetchData()
let firstUserName = 'no user found'
if (data && data.list && data.list.length > 0 && data.list[0].name !== undefined) {
    firstUserName = data.list[0].name
}
文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27023.html
  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/bc/27023.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定