各位小伙伴们,大家好!咱今天就算是正式开张了。实不相瞒,第一期的内容早已写好,但唯独这开篇方式,笔者想了好些时间,包括但不限于如下风格:
- 斗破苍穹式(已经三刷):代码优雅之力,三段!级别:低级!
- 百年孤独式(困扰于错综复杂的人物关系,放弃):多年以后,面对吐槽,我将会回想起,前辈们带我去见识代码规范的那个遥远的下午
- 安娜·卡列尼娜式(仅仅听过开头):好的代码怎么看都优雅,丑的代码则各有各的丑
- 野草·秋夜式(相信我,我是写到这时才刚刚查到这个句式是出自鲁迅先生的这篇文章):我的手中有两种代码,一种是能跑就行的,另一种是别动,动必炸的
- …
就在我兴致勃勃的继续列举时,却尴尬的发现,除了斗破苍穹是在中考在即熬夜看完的,其他的一概没看。加之高中的补课科目中有语文这项,我不得不清醒过来,接受了这样的事实:我令人捉急的语文水平实在不足以撑起想装的野心,遂放弃。
在进入正题之前,先问大家一个问题:大家都玩过街霸吗?想必大多数人都玩过(可能是在游戏厅被父母逮到),没玩过的大概率也听说过,那么大家应该知道有这么个招式:
现在,我们来看看敌人是谁:
不知道大家看过这个逻辑是什么感受,我的第一感觉是:这个方法可以让人有【沉浸式的】阅读体验,你必须不受打扰,非常专注才能(也许不能)读懂这段逻辑。好吧,这是高情商的说法,大家应该也知道了,它的问题是嵌套的太深,缩进太多。
我们只需说明问题即可,就不使用这段逻辑做演示了(事实是笔者偷懒了)。下面,我们用一个相对简单的例子进行说明:
public void notice(int threshold) {if (BooleanUtils.isTrue(isInSilenceTime())) {if (threshold <= 0) {// do something1} else {// do something2}} else {// do something3}
}
上面的例子也是我最近开发遇见的一个case,我需要判断当前时间是否为静默时间,如果是,则继续判断阈值是否有效,如果无效直接返回,如果有效则执行对应逻辑;如果是非静默时间,则执行另一段逻辑。
这段代码有什么问题?它的逻辑相对简单,总不会有什么问题吧?是的,功能上确实没有什么问题,但咱要的是优雅!回看上面的龟派气功,我们知道缩进过多是很讨厌的,因此缩进是我们首要考虑的问题。那么,为什么会造成缩进过多呢?从直接原因上看,是if和else的判断过多;从根本原因上看是我们在写代码时是完全按照自己的思考步骤来写的,但在写完之后没有做美颜。
有一种很常见的处理if-else的手法:通过使用卫语句替换if-else,简单来说,就是一旦发现条件不满足,就直接返回,这样说有些抽象,我们看看用这种手法优化后的代码:
public void notice(int threshold) {if (BooleanUtils.isFalse(isInSilenceTime())) {// do something3return;}if (threshold <= 0) {do something1return;}// do something2}
大家是否觉得现在清爽了很多呢?
- 由于原逻辑中do something2的分支中逻辑较少,因为我们先进行静默时间的判断,如果不满足,直接执行do something2,然后返回
- 考虑完不满足静默时间的逻辑,就只剩满足静默时间的了,因此不再需要else判断
- 如果阈值无效,直接返回,那么阈值有效的情况也不必再使用else了
总的来说,上面case的优化思路就是:
- 提前判断条件,如果命中直接返回,这样会将缩进影响的行数大大减少
- 少用或者不用else
提前判断还比较好理解,但if-else几乎像是亲兄弟,现在居然让我别用?是的,我刚开始都不信,不过这是Jeff Bay在对象健身操中指出的:拒绝使用else关键字,同样震撼的还有其他观点,如:每个方法只使用一级缩进,方法的长度不要超过5行。好吧,我承认目前做不到这种无理的要求,但我们可以试着向这个方向靠拢,让我们再看个例子:
public int getPrice(int userLevel) {int price;if (userLevel == 1) {price = 10;} else if(userLevel == 2) {price = 20;} else if (userLevel == 3) {price = 30;} else {price = 40;}return price;
}
在这个例子中,会根据不同的用户级别返回不同的价格,这一看似乎没什么问题,但想想上面提到的优化思路,优化后的代码如下所示:
public int getPrice(int userLevel) {if (userLevel == 1) {return 10;} if(userLevel == 2) {return 20;} if (userLevel == 3) {return 30;} return 40;
}
怎么样?是否阅读体验更好了呢?在明确了用户级别之后价格就直接返回了,不必再往下看,也不必再担心下面是不是还对价格做了其他的处理,至少在这个例子中,else确实是可以不用的!
还有这种情况也是比较常见的,如果集合不为空,则执行业务逻辑,否则什么都不做。这么写在功能上肯定没问题,但这种写法还是根据我们的思考步骤写的,那我们就可以稍微美化一下:先判断如果集合为空就返回,不为空再写业务逻辑:
优化前:
public void function(List<String> list) {if (CollectionUtils.isNotEmpty(list)) {// 业务逻辑}
}
优化后:
public void function(List<String> list) {if (CollectionUtils.isEmpty(list)) {return;}// 业务逻辑
}
这便是卫语句的威力了,事实上,通过这种手法已经能够改善日常开发中相当多的场景了,至于其他场景我们还可以使用策略模式、封装等方法来进行优化,我们会在后面逐渐看到。
有一个标准来专门衡量我们今天的内容:圈复杂度,它的值越高,代码的复杂性就越高,理解和维护代码的难度也就越大。当然,今天的case只是这个标准的一小部分,感谢兴趣的伙伴们可以自行了解更多。