为什么优秀程序员总在拆函数?因为代码应该表达意图,而不是实现

很多人认为函数的作用是复用代码或减少代码长度,但在《重构:改善既有代码的设计》中,Martin Fowler 提出了一个更重要的观点:函数的意义是表达意图,而不是实现。

当阅读代码时,如果需要花时间理解一段逻辑在做什么,就应该提炼函数,让调用层只表达业务意图,而实现细节隐藏在函数内部。

本文从"重复代码(Duplicated Code)"这一常见坏味道出发,总结三种常见的重构方式:提炼函数、移动语句、函数上移,并通过简单示例说明如何让代码从"读逻辑"变成"读意图"。


一、如何消除重复代码

1. 完全重复:提炼函数(Extract Function)

最简单的重复代码可以直接 提炼函数

scss 复制代码
// bad
if (
  user.age >= 18 &&
  user.emailVerified &&
  !user.suspended
) {
  grantAccess(user)
}
scss 复制代码
// good
if (canUserAccessSystem(user)) {
  grantAccess(user)
}

const canUserAccessSystem = (user) =>
  user.age >= 18 &&
  user.emailVerified &&
  !user.suspended

函数命名表达意图,调用处只需要理解做什么


2. 相似但不相同:移动语句 → 再提炼

有些代码看起来类似,但顺序不同或夹杂其他逻辑。

处理方式:

  1. 移动语句
    将相关的数据和逻辑移动到一起。
  2. 重组结构
  3. 提炼函数

核心思想:

先让代码结构变得一致,再消除重复。


👍移动语句 的目标是改善代码结构,将 数据与相关逻辑聚集在一起

移动时需要注意:

  • 变量依赖顺序
  • 副作用顺序

3. 子类中的重复:函数上移(Pull Up Method)

当多个子类实现了相同的方法时,可以将方法提升到父类。

scala 复制代码
class Engineer extends Employee {
  int getAnnualSalary() {
    return monthlySalary * 12;
  }
}

class Manager extends Employee {
  int getAnnualSalary() {
    return monthlySalary * 12;
  }
}

重构后:

csharp 复制代码
class Employee {
  int getAnnualSalary() {
    return monthlySalary * 12;
  }
}

父类更适合定义共同行为


二、何时应该提炼函数?

很多人认为:

  • 函数是为了复用
  • 函数是为了缩短代码

其实都不是。

作者给了一个很实用的判断方式:

如果你需要花时间浏览一段代码才能弄清它在干什么。

就应该提炼函数。

当然,也可能是你还没读懂代码😁

换句话说:

读代码应该读意图,而不是读逻辑。


三、提炼函数的价值

好的函数命名可以让代码变成一种"叙事结构"。

例如:

scss 复制代码
processOrder()

validateOrder()
calculatePrice()
applyDiscount()
sendEmail()

调用层只表达 业务流程,实现细节隐藏在函数内部。

提炼函数最直接的收益是:

降低阅读成本


四、提炼函数步骤

提炼函数通常可以按下面步骤进行:

  1. 根据代码意图命名函数
  2. 检查变量依赖
    外部变量通过参数传入
  3. 编译代码
  4. 用函数调用替换原代码
  5. 运行测试验证
  6. 查找其他重复代码并替换

如果一开始想不到好名字也没关系。

先提炼函数,在重构过程中名字往往会逐渐清晰。

如果最后发现不合适,可以再 内联回去

这不是无用功。


五、实践技巧

1. 嵌套在源函数

在被提炼代码的源函数内部定义新函数,可以减少参数数量。

javascript 复制代码
function processOrder(order) {
  function calculatePrice() {
    ...
  }
}

注意:

如果有需要,可以提炼到外部


2. 注意变量赋值

如果被提炼代码对变量进行了赋值:新函数应该 返回新的值

如果需要返回多个变量:通常说明这段代码 暂时不适合提炼函数


六、总结

处理重复代码通常有三种方式:

场景 重构方式
完全重复 提炼函数
相似但顺序不同 移动语句 → 再提炼
子类重复实现 函数上移

而提炼函数最核心的原则只有一句话:

代码应该表达意图,而不是实现。 👊


相关推荐
SL_staff1 天前
风控规则不该写代码:一个开发者视角的规则引擎实践拆解
java·架构·全栈
Darling噜啦啦2 天前
前端到上线全流程:从买服务器到 Nginx 反向代理,搞懂全栈部署
nginx·全栈
烬羽2 天前
我把"时间胶囊"部署上腾讯云宝塔,跨域从根源消失了
nginx·node.js·全栈
wangfpp5 天前
生产级 RAG 知识库全流程实践
人工智能·agent·全栈
GitLqr6 天前
从代码复用走向生态:Flutter Dart Package 实战全指南
flutter·全栈·dart
YIAN7 天前
Next.js App Router 全栈实战:从 0 到 1 写一个 Todo 应用,前端后端一个项目搞定
前端·全栈·next.js
浮生望7 天前
Next.js 全栈笔记系统实战:RSC 组件架构、Redis 数据层与规范驱动开发
全栈
GitLqr8 天前
Flutter 实战:如何轻松实现图片与视频保存到手机相册
flutter·全栈·dart
HjhIron8 天前
全栈项目容器化实战:从 React + NestJS 到 Docker 部署与跨域处理
全栈
万敏8 天前
Vue3 全栈实战第七周:错误处理与请求层封装实战记录
vue.js·node.js·全栈