代码重构:过长函数
什么是过长函数(Long Function)
-
定义:一个函数包含了过多的逻辑功能或过分体现逻辑功能的实现细节,导致函数产生过长的代码块
-
影响:过长的函数往往意味着函数功能不单一;或过分呈现细节未进行抽象。造成可读性差和增加代码维护成本;过长函数还容易发生资源未释放等编码问题
-
改进目标:抽象待重构函数流程,分解出子函数,最大化精简出可读性强、功能单一的函数
-
方法:提炼函数:找到函数中适合集中在一起的部分,将他们提炼出来形成一个新函数,并用函数“做什么”来命名新的函数
代码案例
-
代码背景:一个打印发票的函数,发票的内容包含三块:
-
打印发票头。发票头包含买方、卖方姓名;
-
打印产品明细列表。每包含行产品号、名称、数量和价格信息;
-
打印发票汇总。输出总金额。
-
-
症状/问题:函数功能逻辑实现过于呈现细节,导致函数过长,理解难度大:
-
函数较长且过分呈现实现细节,理解函数意图困难
-
代码实现解耦性差,for循环复杂,包含两个功能
-
代码实现语句理解难,if条件复杂,不能够快速理解条件含义
-
临时变量过多,for循环中使用了过多的不必要临时变量
-
类功能未内聚,属于Invoice和InvoiceLine的函数未内聚。例如计算总价不应属于打印器的能力
-
-
重构目标:简化print函数实现流程,提高可读性
(1)理解长函数功能意图,归纳功能流程,提炼功能步骤
-
理解函数意图
-
寻找注释:注释间的代码块可能表明了代码可以分块提炼。同时注释通常能指出代码意图,帮助提炼函数的命名
-
若无注释,阅读理解长函数实现功能,归纳长函数的执行步骤
-
-
归纳功能流程
-
将属于同一逻辑功能的代码通过移动语句聚合到一起 printHeader
printDetail
-
printTableHeader
-
printInvoiceLines
printFooter
-
-
-
功能逻辑分割 可以看出printDetail步骤中夹杂了计算所有商品总价的功能,为帮助提炼函数,必须解耦函数算法的逻辑
-
for功能逻辑分割
-
for循环包含功能:
-
打印发票正文的每行信息
-
计算所有产品总价
-
注:产品总价实际服务于最后的Total打印,分割出的for循环需要移动语句到对应位置
-
提炼函数抽象主函数流程
-
注:以“做什么”命名提炼的函数,而不是“怎么做”
(2)复杂语句下沉到子函数,对复杂子函数递归重构–printDetail函数
-
子函数进行流程分析和提炼
-
临时变量过多:以查询取代变量
-
功能内聚到合适的位置,并以查询取代变量
-
分解条件语句:复杂的条件表达式
-
分解条件语句:复杂的条件分支语句:如果条件分支语也有过长语句的坏味道,可继续进行递归重构。
-
注:对于双分支的条件语句可使用三目运算符exp1?exp2:exp3精简
代码案例2
-
代码背景:这是一个保存数据的方法,根据用户指定的保存方式和保存目的地址进行文件的上传或保存。保存方式包括:本地保存、SFTP上传、EMAIL发送、华为云、百度云方式
-
症状/问题:函数功能不单一且实现过于呈现细节,导致函数过长且需翻页阅读
-
过于呈现实现细节,导致功能不单一:包含分类工作和具体的保存/上传操作
-
函数太长,导致显示屏不能一屏显示,造成阅读和理解的困难
-
后续的维护将使该函数不断增大,例如增加保存/上传类型
-
-
重构目标:简少save函数代码行,方便用户一页显示,提高可读性
-
Switch分支语句的提炼&重构前后对比
重复的Switch语句:特别地,如果统一Switch分支条件模式出现了多次,可使用多态取代重复的switch进行重构。
重构对比
重构前
-
Print打印函数功能复杂、函数过长、代码理解难度高
-
InvoicePrinter类功能不单一,print函数内部做了非本类的职责
重构后
-
各函数短小精悍,能快速定位到子功能函数进行维护修改
-
函数职责单一,
-
相对来说函数功能清晰,极易理解代码逻辑和流程
相关技巧
识别工具:华为通用编码规范
规范从表象上要求对于一般超过50行的函数需要优先进行过长函数的重构,可通过IDE端侧CodeCheck进行识别超过50行的函数: 选中目录或文件-> 右击-> Code Check -> Full Check -> 等待结果
注:不仅仅超过50行的才需要过长函数的重构,仅仅是优先项,只要函数过于呈现业务细节,影响了可读性和可维护性,就应该通过过长函数的重构手法进行重构
- 点赞
- 收藏
- 关注作者
评论(0)