重构读书笔记九——简化条件逻辑

重构 Jan 12, 2021

概述

程序的大部分逻辑来自条件逻辑,但很不幸,程序的复杂度大多也来自于条件逻辑。通过重构手法可以让条件逻辑更容易理解。

9.1 分解条件表达式

描述

将复杂的条件表达式用函数提炼出来,让函数赋予条件表达式语义,方便理解。

代码

if (!aDate.isBefore(plan.summerStart) && !aDate.isAfter(plan.summerEnd))   
  charge = quantity * plan.summerRate; 
else  
  charge = quantity * plan.regularRate + plan.regularServiceCharge;

refactor

if (summer())  
  charge = summerCharge();  
else  
  charge = regularCharge();

动机

程序之中,复杂的条件逻辑是经常导致复杂度上升的地点之一。我们应该将复杂的条件逻辑分解成独立的函数,从而更清楚地表达自己的意图。

9.2 合并条件表达式

描述

将最终行为一致的表达式用逻辑或,逻辑与合并到一个条件判断里面。

动机

因为不要重复,这种逻辑是典型的重复,然后就是为以后提炼函数做准备。

9.3 以卫语句取代嵌套表达式

描述

经常看到这样的代码:一个if嵌套另一个if,有多达五层的嵌套。

代码

function getPayAmount() {   
  let result;  
 
  if (isDead)   
    result = deadAmount();   
  else {   
    if (isSeparated)    
      result = separatedAmount();    
    else {    
      if (isRetired)     
        result = retiredAmount();     
      else     
        result = normalPayAmount();   
      }
  }  
  return result; 
}

refactor:

function getPayAmount() {  
  if (isDead) 
    return deadAmount();  
  if (isSeparated) 
    return separatedAmount();   
  if (isRetired) 
    return retiredAmount();   
  return normalPayAmount(); 
}

动机

条件逻辑有两种情况,每个if-else-then都是正常的业务逻辑,这样的条件结构是合理的(但是也不应该过多,可以用多态和工厂方法取代)。另外一种是一个分支合理,另外一个不合理的异常情况。在这种情况下不合理的情况应该尽早处理并从业务中结束。在正常逻辑之前把异常情况都处理掉的语句书中称为卫语句(我一般叫做防御编程)。只保留正常的业务逻辑让业务代码更清晰,同时可以有效减少逻辑判断的层数。

9.4 用多态取代条件表达式

描述

通过工厂方法来隐藏条件逻辑,利用多态来复用共有逻辑,再利用类的封装特性来屏蔽不同的业务逻辑。

动机

[me]:用多态和工厂方法,如果具有良好的ddd实践和依赖注入的思想,可以提供耦合度很低的业务代码,并且对扩展非常方便。在做dsp平台的时候,要对接很多adx平台,对于dsp平台来说,对内部流程的抽象,可以抹平不同adx平台的差异。后续添加新的adx平台只需要关注接口的对接即可。代码复用率很高。
复杂的条件逻辑是编程中最难理解部分之一。作者发现可以将条件逻辑拆分到不同的场景(或者叫高阶用例),从而拆解复杂的条件逻辑。这种拆分逻辑有时用条件逻辑本身的结构就足以表达,但使用类和多态能把逻辑的拆分表达得更清晰。

9.5 引入特例

描述

作者这里是希望用一个特例来取代null对象,或者一个特殊的对象来代表一种特殊情况。用这个对象来取代所有的类似操作(判空,不处理等)。
[me]:现在编程语言提供了option类似的语法来让我们以一种更优雅的方式处理空对象。

动机

一种常见的重复代码是:一个数据结构的使用者都在检查某个特殊的值,并且当这个特殊值出现时,所做的处理也都是一样的。这种情况,我会把这个逻辑都收拢到一处。处理这种情况的一个好办法是使用特例模式:创建一个特例元素,用以表达对这个特例的公用行为的处理。

9.5 引入断言

描述

断言从c语言乃至更好就存在的语法,说实话用的不是很多。在spring和一些项目的源码里面见过使用,对于在公司的项目从来没有使用。

动机

如果你发现代码假设某个条件始终为真,就加入一个断言明确说明这种情况。

总结

无论学会了什么样的方法技巧,其实关键不是对技巧的掌握,而是深刻理解其中体现的思想。重构的思想,面向对象的思想,设计模式的思想,ddd的思想。回到本源是封装(语义化,不要和陌生人谈话...),抽象(高阶函数,算子,领域划分),设计模式六大规则,依赖注入(解耦的方式)等等,掌握好这些自然而然就能写出好的代码了。