重构读书笔记五——重构名录

重构 Dec 28, 2020

概述

从这一章开始作者开始介绍和详细描述前面提到的重构方法。整理下来就是标题提到的重构名录。

重构方法的格式

作者在介绍重构方法时候采用了一种格式,包含以下5个部分。

  1. 名称(name):名称是很重要的,不仅仅是作为一个唯一表示,而且名称很大程度上代表了方法的含义。
  2. 速写(sketch):通过代码来展示重构的改变
  3. 动机:这么需要这个重构,还有什么情况下不该做这个重构
  4. 做法:简明扼要的一步步介绍如何进行重构
  5. 范例:用一个简单的例子来说明重构手法如何使用。

5.1 提炼函数(extract function)(106)

反向重构:内联函数(115)

速写

function printOwing(invoice) {  
  printBanner();  
  let outstanding = calculateOutstanding();  
  //print details  
  console.log(`name: ${invoice.customer}`);  
  console.log(`amount: ${outstanding}`); 
}

refactor:

function printOwing(invoice) {  
  printBanner();  
  let outstanding = calculateOutstanding();  
  printDetails(outstanding);  
  
  function printDetails(outstanding) {   
    console.log(`name: ${invoice.customer}`);   
    console.log(`amount: ${outstanding}`);  
  } 
}

动机

这个重构方法的关键是什么时候将一段代码放到一个方法中?作者的观点是将意图与实现分开。如果你需要花时间去浏览一段代码才能明白它是做什么?那么就应该将它放到一个函数中。
[me]:我的观点是应该将表达一个概念或者动作或者一个语义的代码放到一个方法里面。并且尽可能遵守kiss原则,一个函数只干一件事情。

5.2 内联函数(115)

反向重构:提炼函数

速写

function getRating(driver) {  
  return moreThanFiveLateDeliveries(driver) ? 2 : 1; 
} 
function moreThanFiveLateDeliveries(driver) {  
  return driver.numberOfLateDeliveries > 5;
}

refactor:

function getRating(driver) {  return (driver.numberOfLateDeliveries > 5) ? 2 : 1; }

5.3 提炼变量

反向重构:内联变量(123)

速写

return order.quantity * order.itemPrice -  Math.max(0, order.quantity - 500) * order.itemPrice * 0.05 +  Math.min(order.quantity * order.itemPrice * 0.1, 100);

refactor:

const basePrice = order.quantity * order.itemPrice; 
const quantityDiscount = Math.max(0, order.quantity - 500) * order.itemPrice * 0.05; 
const shipping = Math.min(basePrice * 0.1, 100);
return basePrice - quantityDiscount + shipping;

动机

太长的表达式虽然在逻辑上没有问题,但是让人难以理解。将表达式的中间有意义的变量抽离出来,命名可以让更易于理解。

5.4 内联变量

反向重构:提炼变量(119)

速写

let basePrice = anOrder.basePrice; 
return (basePrice > 1000);

refactor:

return anOrder.basePrice > 1000;

动机

没事干的重复变量

5.5 改变函数声明

关键就是恰当的声明函数的作用。unix和linux中的工具大部分都遵循了kiss原则,因此他们的功能和名字是相关的。

5.6 封装变量

大部分语言都没有在语言层面提供代理或者aop的编程方法。所以当我们处理数据的时候最好将访问的功能进行一层封装。这样当我们想要重构代码的时候就编程重构这个函数就好了。

5.7 变量命名

变量的命名要有意义,不要a,b,c这种没有意义的命名。

5.8 引入参数对象

如果一个数据总是伴随着另一个数据传入参数,那么他们就应该用一个数据结构来表示。如果他们在业务就是必须成对出现的就更需要了。比如说计算工资的时候要伴随着的税率。一段时间的开始和结束。年月日,放在一起作为datetime的数据结构。

5.9 函数组合成类

把操作同样数据结构或者同一个领域的函数,放到一个类里面。一般这个类我叫做service类,但是这个类不要持有数据。数据要和动作分离。
数据无关的代码更通用,不绑定数据也就和业务实现一定程度的隔离,这样的代码更易于重构和扩展。

5.10 函数组合成变换

将多个函数组合,然后放到一个函数里面,我一般叫这个函数为一个变换。这样的好处是将多个函数的业务或者逻辑单元组合,然后提高到更高层次的业务或者逻辑中。

5.11 拆分阶段

每当看见一段代码在干两件事情,还在一个函数中,我都想把它拆分成各自独立的模块。

总结

这章对基础的重构方法进行总结,有些地方我感觉很直观容易理解的方法就直接概括了,没有按照目录规则来写。后面几章会讲解封装,继承等重构方法。另外第四部分应该是关于重构和测试,但是作者这里写的比较零散,后面我打算和tdd和单元测试,以及测试方法一起写下。