wangpin34 / blog

个人博客, 博文写在 Issues 里
5 stars 0 forks source link

2021/06/25: 减少 if else 的办法 #64

Open wangpin34 opened 3 years ago

wangpin34 commented 3 years ago

封面图

本周话题: 减少 if else 的办法

程序逻辑复杂之后,代码里经常会堆叠不少 if else 语句,像这样:

if a {
  if a1 {

  } else if a2 {

  } else {

  }
} else if b {

} else if c {

} else {

}

大量的 if else 语句看起里杂乱,阅读起来也难以理解,有什么办法可以减少呢?最直接的办法当然是使用 switch 语句替代 if else, 如:

swift condition {
  case a: 
    do something
  case b: 
    do something
  case c: 
    do something
  default: 
    do something
}

相比前者, switch 的写法逻辑结构更紧凑,使得逻辑更清晰。但也牺牲了一部分便利性,比如判断条件不如 if else 语句灵活,而且嵌套很麻烦。

那还有其他方法解决这个问题吗?有,这就是逻辑数据化。最经典的应用是用户权限。

假设一个逻辑:系统用户(system-user)可以删除(delete),修改(update),浏览(read),普通用户(user)只能浏览。为了演示简单,我们只关心对应按钮是否被禁用,默认都是禁用状态(disabled=true),如果刚好有权限,就设定为 false。

if else 的写法,大概是:

if system-user {
   deleteButton.disabled = false
   editButton.disabled = false
   viewButton.disabled = false
} else if user {
   viewButton.disabled = false
}

逻辑数据化,就是将逻辑内化到配置数据里,比如,配置 system-useruser的权限数据,如下

{
  "system-user": {
     "delete": true,
     "update": true,
     "read": true
   },
  "user": {
    "read": true
  }
}

在进行业务决策,比如是否禁用 delete 按钮,参考对应 permission 的即可,不需要甄别用户类型。

   deleteButton.disabled = !permission.delete
   editButton.disabled =  !permission.update
   viewButton.disabled =  !permission.read

逻辑数据化 的核心思想是,将逻辑上的分支转化为不同的配置数据。因为不同转移到数据,所以代码逻辑就会变得统一。比如上文这样,只要做好配置的导入和应用,并不需要甄别用户类型再去处理。而且,未来如果用户权限又有新规则,只需要扩展数据,代码适配新数据。

从 Rxjs 到 Rxswift

我是从去年开始学习 Rxjs,用它改造了几个老项目。很多人评价说 Rxjs 引入了太多抽象概念,不够直观,debug 也困难,从我自己的体验来说,是的,上手难度的确偏高。但是我当初还是决定学习 Rxjs,原因很简单,我想寻找与 React 松耦合的状态管理办法。 Redux 虽然声称与具体 UI Layer 无关,但是写起来太啰嗦。 Mobx 和 Recoil 要么与 JS 绑定,要么与 React 绑定。我的想法还是想学习一套可以跨平台的解决方案。 Rx 系列比较满足我对跨平台的期待。

目前看起来,我的决定是正确的,最有力的证据是,当我开始写 ios app 时,依然可以吃到学习 Rxjs 的红利: RxSwift 对我来说“太简单了”,我需要关注的仅仅只是接口层面的细微差异,Observable,Subject,Operator,这些核心概念是完全一致的。

所以比较省力的学习方法,尤其是在编程领域里,就是学习可以跨平台的方案,比如 Rx,而不是与平台紧密结合的,比如 Recoil。那问题来了,为什么我学习 swift 而不是 flutter(准确的讲还是学了一部分的)呢?因为在我目前的认知里,mobile app 还是 native 给力,而且, swift 很好玩。

TypeScript

坦白说,TypeScript 现在已经成为我的路径依赖,如果一个项目里没有使用 TypeScript,我就觉得完全看不懂,非得加上Types不可。代价是刚刚开始进度会很慢,因为要花时间理解那些不是自己开发的程序到底对数据类型有什么要求,然后据此编写 Types。随着 Types 的完善,开发体验和整体效率提升上来,到后期,依靠 lint,80%的脑力可以节省下来放空/神游,而不必时刻记忆着各种函数对入参的要求。这样看来,前期的准备确实起到了”磨刀不误砍柴工“的效果。

读完了《西游记》

《西游记》我几年前尝试读过,看了几页就放弃了,相比于《三国演义》(读了很多遍〉的偏紧凑的结构,《西游记》里面有很多大段写景,写人物外形,或者动不动就从盘古开天开始回忆的段落。这是我当初放弃的原因。我还是喜欢比较“干”的故事,对那些人物穿什么,住在什么地方,或者祖上阔不阔,真的没有感觉。

我之所以这次读完了整本,是因为我想通了一件事情,作为读者,主动应该在我。不喜欢看的部分,直接翻过就好了嘛。这样一来,我就坚持了下来。从拜师学艺,到大闹天宫,再到被压五行山,一路向前,大概花了半个月时间,看到师徒四人取得真经。《西游记》确实配得上四大名著,我从里面看到了太多东西,这里就不一一说明了。

读书这件事情本身给我的启示是,我们很容易自我设限,必须这样,必须那样,就像曾经的我对着厚厚的《西游记》无处下手。其实改变很简单,就是把那些妨碍自己的都扔在一边。

我想,如果我这辈子都没能学会硬着头皮读那些长长的段落,或者我始终没有勇气决定把那些长长的段落抛在脑后,那么,我现在依然对西游记的真相一无所知。

而现在呢?虽然绕开了那些可能宝贵的文字,我依然得到了大部分的信息,也对很多人的所谓真相产生了自己的怀疑,这是一件好事,预示着我会更多的思考,更多的重复阅读。也许就在着不断的回顾中,我会发现那些被绕开的文字竟然有特比的意义,也说不定呢。