第一组重构(下):改变函数声明、封装变量、变量改名、引入参数对象
好,咱们接着聊第一组重构的下半部分。上一节我们讲了提炼函数和搬移语句,这次要聊的几个手法,说白了都是关于「名字」和「结构」的调整。你想想看,代码里最让人头疼的是什么?不是算法复杂,而是你看到一个变量名,完全猜不出它是干嘛的。
我个人习惯是,写代码的前十分钟,可能有一半时间在改名字。别笑,这很值得。
改变函数声明
这个重构手法,以前叫「函数改名」,后来 Martin Fowler 觉得它不止改名字,还包括改参数列表,所以改成了现在的名字。但我个人觉得,最核心的还是改名字。
函数的名字,就是它的说明书。一个函数叫 calc(),你看了完全不知道它在算什么。改成 calculateTotalPrice(),是不是清晰多了?
我在项目中遇到过一段代码,一个函数叫 process(),里面处理了订单、发送邮件、更新库存。后来新同事接手,完全不敢动,因为不知道这个「process」到底包含了什么。我们花了半天把它拆成三个函数,每个都取了明确的名字,问题迎刃而解。
改参数列表也是常见场景。比如一个函数需要三个参数,但调用方每次都只传两个,第三个永远是默认值。这时候就该考虑把第三个参数去掉,或者用参数对象替代。
// 改名前
function setDimensions(width, height, depth) {
// ...
}
// 改名后
function setDimensions(dimensions) {
// dimensions 是一个对象
}
封装变量
封装变量,说白了就是把一个公开的变量藏起来,通过函数来访问。为什么要这么做?因为直接暴露变量,你就失去了对它的控制。
举个例子,你有一个全局变量 let userCount = 0;,到处都在直接修改它。有一天你想加个日志,记录每次修改的时间和调用方,怎么办?一个个去找?太累了。
封装之后,你只需要在 setter 里加一行代码:
let _userCount = 0;
function getUserCount() {
return _userCount;
}
function setUserCount(value) {
console.log(`userCount 被修改为 ${value}`);
_userCount = value;
}
我曾经在一个遗留系统里看到过,一个全局变量被 47 个地方直接修改。你想加个校验,根本无从下手。后来我们花了两周,把所有直接访问都改成了函数调用。嗯,那两周确实痛苦,但改完之后,系统稳定了很多。
变量改名
这个重构手法看起来最简单,但实际做起来,影响往往最大。变量名是代码里出现频率最高的东西,一个坏名字会反复误导你。
我见过最离谱的变量名:let a = 10;,然后整个函数里用了 20 次这个 a。你根本不知道它代表什么。后来我们改成 let maxRetryCount = 10;,所有人都松了一口气。
变量改名的原则很简单:
- 名字要能表达意图:
let d = 0;不如let elapsedDays = 0; - 避免缩写:
usrCnt不如userCount,省不了几个字符,但阅读成本高很多 - 保持一致性:如果其他地方用
customer,这里就别用client
// 坏名字
let t = new Date();
// 好名字
let startTime = new Date();
引入参数对象
这个重构手法,专门对付「参数过多」的问题。你想想看,一个函数有六七个参数,调用的时候谁记得住顺序?
引入参数对象,就是把一组经常一起出现的参数,封装成一个对象。比如:
// 重构前
function createBooking(customerName, customerEmail, customerPhone, startDate, endDate) {
// ...
}
// 重构后
function createBooking(customer, dateRange) {
// ...
}
这里 customer 包含了姓名、邮箱、电话,dateRange 包含了开始和结束日期。参数从 5 个变成了 2 个,清晰多了。
我在项目中遇到过最夸张的一个函数,有 12 个参数。每次调用都要翻文档,看看第 5 个参数是什么。后来我们引入了三个参数对象,函数签名变成了 3 个参数,调用方也开心了,维护方也开心了。
- 三个以上参数经常一起出现
- 参数之间有逻辑关联(比如地址相关的几个字段)
- 调用方经常需要查文档才能知道参数顺序
知识体系总览
下面这张图,把这四个重构手法的关系和适用场景梳理了一下。你可以把它当作一个快速参考。
避坑指南
最后,分享几个我踩过的坑:
- 改名字时不要只改一处:我曾经改了一个变量名,但忘了改注释,结果注释和代码对不上,比不改还糟糕。
- 封装变量时不要过度:如果一个变量只在函数内部使用,没必要封装成 getter/setter,直接改名字就行。
- 引入参数对象时不要塞太多:参数对象里的字段应该逻辑相关,不要把不相关的硬凑在一起。
好了,这一组重构手法就聊到这里。记住,重构不是一次性的工作,而是日常开发的一部分。每次看到代码不顺眼,就顺手改一下。积少成多,代码质量会越来越好。