恋上蓝花楹

你的函数为什么要做那么多事?:代码整洁之道

你是否见过这样的代码?一个函数长达几百行,包含数据验证、数据库查询、业务逻辑处理、文件操作、发送通知……仿佛一个全能的瑞士军刀,什么事情都往里塞。这就是我们要讨论的问题——你的函数为什么要做那么多事?

单一职责原则:代码整洁的基石

在面向对象设计领域,有一个鼎鼎大名的原则叫做「单一职责原则」(Single Responsibility Principle)。它的大意是:每个类、每个函数应该只有一个引起它变化的原因。翻译成大白话就是:一个函数只做一件事,而且把这件事做好。

为什么这很重要?想象一下,如果你家的厨房只有一个多功能电器——它同时是微波炉、烤箱、电饭煲、榨汁机。当榨汁机坏了,你就要整机维修;当你想升级微波炉时,整个电器都得换。这种「紧耦合」的设计会让你的生活一团糟。代码也是如此。

如何判断函数是否做了太多事?

这里有几个简单的判断标准:

  • 函数名无法准确描述功能:如果你发现函数名叫 processUserDataAndValidateAndSaveToDbAndSendEmail,那它明显做了太多事。
  • 需要用「并且」来描述函数功能:「这个函数用来处理订单并且验证库存并且计算价格并且发送通知……」当你在描述时需要说「并且」,说明该拆分了。
  • 超过100行:虽然代码行数不是绝对标准,但如果一个函数超过100行,通常意味着它承担了太多职责。
  • 难以测试:如果你发现测试一个函数需要准备十几种不同的场景和数据,说明它的职责过于复杂。

重构实战:一个订单处理函数的蜕变

让我们来看一个实际例子。假设你有一个这样的函数:

function processOrder(order) {
  // 验证订单
  if (!order.items || order.items.length === 0) {
    throw new Error("订单不能为空");
  }
  // 检查库存
  for (const item of order.items) {
    const stock = db.query("SELECT stock FROM products WHERE id = ?", item.id);
    if (stock < item.quantity) {
      throw new Error("库存不足");
    }
  }
  // 计算价格
  let total = 0;
  for (const item of order.items) {
    total += item.price * item.quantity;
  }
  // 应用折扣
  if (order.coupon) {
    total *= 0.9;
  }
  // 保存订单
  db.execute("INSERT INTO orders ...");
  // 发送邮件
  emailService.send(order.user.email, "订单确认");
  // 记录日志
  logger.info("Order processed: " + order.id);
}

这个函数做了多少件事?粗略一数:验证、查库存、计算价格、应用折扣、保存订单、发送邮件、记录日志。足有七件事!

经过单一职责原则重构后:

function processOrder(order) {
  validateOrder(order);
  checkInventory(order.items);
  const total = calculateTotal(order);
  saveOrder(order, total);
  sendOrderConfirmation(order);
  logOrderProcessed(order.id);
}

function validateOrder(order) {
  if (!order.items || order.items.length === 0) {
    throw new Error("订单不能为空");
  }
}

function calculateTotal(order) {
  let total = order.items.reduce((sum, item) => sum + item.price * item.quantity, 0);
  return order.coupon ? total * 0.9 : total;
}

// ... 其他函数各自负责单一职责

重构后的代码,每个函数都「干净」了:名字清晰、功能单一、易于测试、便于维护。如果将来需要修改折扣逻辑,你只需要动 calculateTotal 函数,完全不用担心会影响库存检查或邮件发送。

写在最后

写代码如同烹饪。最开始,你可能只能做出一道「乱炖」——把所有食材都扔进锅里,虽然能吃,但味道实在不敢恭维。随着经验积累,你会学会把食材分开处理:肉是肉、菜是菜、汤是汤,最后再巧妙地组合在一起。这就是代码的「厨艺」。

下次当你准备在一个函数里添加新功能时,先问问自己:这件事应该由另一个函数来做吗?让你的函数「只做一件事」,你会发现代码变得可读、可维护、可测试,而你自己也会成为更好的程序员。

毕竟,在代码的世界里,「简单」才是真正的「复杂」。

wulilele

我是一名热爱科技与AI的软件工程师。