当前位置: 首页 > news >正文

重构改善既有代码的设计-学习(四):简化条件逻辑

1、分解条件表达式(Decompose Conditional)

 

        可以将大块代码分解为多个独立的函数,根据每个小块代码的用途,为分解而得的新函数命名。对于条件逻辑,将每个分支条件分解成新函数还可以带来更多好处:可以突出条件逻辑,更清楚地表明每个分支的作用,并且突出每个分支的原因。

        例如:

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

         改为:

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

2、合并条件表达式(Consolidate Conditional Expression)

        一串条件检查:检查条件各不相同,最终行为却一致。如果发现这种情况,就应该使用“逻辑或”和“逻辑与”将它们合并为一个条件表达式。 

        将检查条件提炼成一个独立的函数对于厘清代码意义非常有用,因为它把描述“做什么”的语句换成了“为什么这样做”。

        如果这些检查的确彼此独立,的确不应该被视为同一次检查,就不能使用本项重构。 

        重构例子:

if (anEmployee.seniority < 2) return 0;
if (anEmployee.monthsDisabled > 12) return 0;
if (anEmployee.isPartTime) return 0;

        改为:

if (isNotEligibleForDisability()) return 0;
function isNotEligibleForDisability() {return ((anEmployee.seniority < 2)|| (anEmployee.monthsDisabled > 12)|| (anEmployee.isPartTime));
}

 3、以卫语句取代嵌套条件表达式(Replace Nested Conditional with Guard Clauses)

 

         如果某个条件极其罕见,就应该单独检查该条件,并在该条件为真时立刻从函数中返回。
这样的单独检查常常被称为“卫语句”(guard clauses)。

         至于“单一出口”规则,其实不是那么有用。保持代码清晰才是最关键的

        例子:

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

        改为:

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

 4、以多态取代条件表达式(Replace Conditional with Polymorphism)

         复杂的条件逻辑是编程中最难理解的东西之一,因此我一直在寻求给条件逻辑添加结构。很多时候,我发现可以将条件逻辑拆分到不同的场景(或者叫高阶用例),从而拆解复杂的条件逻辑。这种拆分有时用条件逻辑本身的结构就足以表达,但使用类和多态能把逻辑的拆分表述得更清晰。

        一个常见的场景是:我可以构造一组类型,每个类型处理各自的一种条件逻辑。例如,我会注意到,图书、音乐、食品的处理方式不同,这是因为它们分属不同类型的商品。最明显的征兆就是有好几个函数都有基于类型代码的switch语句。若果真如此,我就可以针对switch语句中的每种分支逻辑创建一个类,用多态来承载各个类型特有的行为,从而去除重复的分支逻辑。

        另一种情况是:有一个基础逻辑,在其上又有一些变体。基础逻辑可能是最常用的,也可能是最简单的。我可以把基础逻辑放进超类,这样我可以首先理解这部分逻辑,暂时不管各种变体,然后我可以把每种变体逻辑单独放进一个子类,其中的代码着重强调与基础逻辑的差异。

        多态是面向对象编程的关键特性之一。跟其他一切有用的特性一样,它也很容易被滥用。我曾经遇到有人争论说所有条件逻辑都应该用多态取代。我不赞同这种观点。我的大部分条件逻辑只用到了基本的条件语句——if/else和switch/case,并不需要劳师动众地引入多态。但如果发现如前所述的复杂条件逻辑,多态是改善这种情况的有力工具。

        例子:

switch (bird.type) {case 'EuropeanSwallow':return "average";case 'AfricanSwallow':return (bird.numberOfCoconuts > 2) ? "tired" : "average";case 'NorwegianBlueParrot':return (bird.voltage > 100) ? "scorched" : "beautiful";default:return "unknown";
}

         改为:

class EuropeanSwallow {get plumage() {return "average";}
}
class AfricanSwallow {get plumage() {return (this.numberOfCoconuts > 2) ? "tired" : "average";}
}
class NorwegianBlueParrot {get plumage() {return (this.voltage > 100) ? "scorched" : "beautiful";}
}

 5、引入特例(Introduce Special Case)

 

         一个数据结构的使用者都在检查某个特殊的值,并且当这个特殊值出现时所做的处理也都相同。如果我发现代码库中有多处以同样方式应对同一个特殊值,我就会想要把这个处理逻辑收拢到一处。

        处理这种情况的一个好办法是使用“特例”(Special Case)模式:创建一个特例元素,用以表达对这种特例的共用行为的处理。这样我就可以用一个函数调用取代大部分特例检查逻辑。

        例子: 

if (aCustomer === "unknown") customerName = "occupant";

         改为:

class UnknownCustomer {get name() {return "occupant";}
}

6、引入断言(Introduce Assertion) 

 

        常常会有这样一段代码:只有当某个条件为真时,该段代码才能正常运行。

        这样的假设通常并没有在代码中明确表现出来,你必须阅读整个算法才能看出。有时程序员会以注释写出这样的假设,而我要介绍的是一种更好的技术——使用断言 

        断言是一个条件表达式,应该总是为真。如果它失败,表示程序员犯了错误。断言的失败不应该被系统任何地方捕捉。整个程序的行为在有没有断言出现的时候都应该完全一样。实际上,有些编程语言中的断言可以在编译期用一个开关完全禁用掉。 

        断言是一种很有价值的交流形式——它们告诉阅读者,程序在执行到这一点时,对当前状态做了何种假设。另外断言对调试也很有帮助。而且,因为它们在交流上很有价值,即使解决了当下正在追踪的错误,我还是倾向于把断言留着。

        例如:

if (this.discountRate)base = base - (this.discountRate * base);

        改为:

assert(this.discountRate>= 0);
if (this.discountRate)base = base - (this.discountRate * base);

 

 

http://www.lryc.cn/news/287429.html

相关文章:

  • 【代码---利用一个小程序,读取文件夹中图片,将其合成为一个视频】
  • MVC 和 MVVM的区别
  • redis—Set集合
  • 【jetson笔记】vscode远程调试
  • 大数据处理流程包括哪些环节
  • C++入门篇章1(C++是如何解决C语言不能解决的问题的)
  • java复习篇 数据结构:链表第一节
  • 深入理解与运用Lombok的@Cleanup注解:自动化资源管理利器
  • 【LeetCode每日一题】2865. 美丽塔 I
  • Cute Http File Server 使用文章
  • c#算法(10)——求点到直线的距离
  • [小脚本] maya 命令行常用操作
  • 数据结构·单链表
  • Redis(秒杀活动、持久化之RDB、AOF)
  • Window安装Python和开发Pycharm
  • 技术驱动宠物健康:宠物在线问诊系统的高效搭建手册
  • 玩转k8s:yaml介绍
  • 【spdk】spdk compressdev测试
  • Linux中并发程序设计(进程的创建和回收、exec函数使用)
  • 2023年DevOps国际峰会暨 BizDevOps 企业峰会(DOIS北京站):核心内容与学习收获(附大会核心PPT下载)
  • pdf 转html 在线预览和查询
  • docker 体验怀旧游戏(魂斗罗等)
  • JS中判断数据类型总结以及方法封装
  • 【Midjourney】绘画风格关键词
  • 教你如何低成本自建「幻兽帕鲁」服务器,快速一键部署
  • 拥抱社交电商浪潮:微信小程序商城崛起引领电商新风向-亿发
  • 一个使用pyqt的word文档查重工具
  • SpringCloud Alibaba Sentinel 与 SpringCloud Gateway 的限流有什么差别?(三种限流算法原理分析)
  • 邦芒忠告:职场新人最需要避开的十大雷坑
  • MySQL-进阶-索引