重构 If 并不意味着消除决策

发布: (2025年12月15日 GMT+8 07:57)
5 min read
原文: Dev.to

Source: Dev.to

引言

我已经介绍过 Object Maps,这是一种强大的技术,用来替代 switchif/else 链。使用它,我们用常数时间(O(1))的直接访问取代了循环复杂度,使其非常适合键→值的静态映射场景。

然而,现实中的软件开发很少如此可预测。我们要处理动态业务规则、数值区间、组合校验以及依赖上下文的决策,这些情况下单纯的映射已经不够用了。

于是就出现了不可避免的疑问:

if,我注定要写脏代码吗?

答案是 。重构条件语句并不是一场看谁代码行数更少的竞技(要是这样,只要用压缩工具就行了 :D),而是看谁的代码更清晰。我们的目标不仅仅是语法层面:更重要的是停止编写防御性条件,开始编写表达意图的代码。

防御性和嵌套代码

// ❌ 防御性和嵌套代码
function processarPedido(pedido) {
  if (pedido) {
    if (pedido.ativo) {
      if (pedido.itens.length > 0) {
        if (pedido.saldo >= pedido.total) {
          // 最终,真正的业务逻辑...
          console.log("Processando...");
        }
      }
    }
  }
}

这里的问题不是校验本身,而是认知负担。你必须阅读多行“噪音”才能找到业务逻辑。

Guard Clauses(提前返回)

组织代码的第一种策略是 提前返回(或 Guard Clauses)。规则很简单:先处理异常情况。如果某个条件阻止代码继续执行,就立刻返回。这消除了 else 的需求,并去掉了层层缩进。

// ✅ Guard Clauses:清理流程
function processarPedido(pedido) {
  if (!pedido || !pedido.ativo) return;
  if (pedido.itens.length === 0) return;
  if (pedido.saldo = 18 && user.hasLicense && !user.suspended) {
    rentCar();
  }
}

意图阅读(谓词)

// ✅ 意图阅读(谓词)
const podeAlugarCarro = (user) =>
  user.age >= 18 && user.hasLicense && !user.suspended;

if (podeAlugarCarro(user)) {
  rentCar();
}

if 仍然在,但复杂度已经抽象到一个具备表达性名称的函数中,便于以后阅读。

Switch 作为工厂(Strategy)

很多时候复杂度不是布尔型(是/否),而是分类的(类型 A、类型 B、类型 C)。在这种情况下 Object Map 很好用,但如果每种类型需要不同的逻辑,switch 可能会变成代码意大利面怪兽。

错误用法(耦合逻辑)

// ❌ 耦合逻辑
function calcularFrete(tipo, peso) {
  switch (tipo) {
    case 'SEDEX':
      const taxa = obterTaxa();
      return (peso * taxa) + 10;
    case 'PAC':
      if (peso > 30) throw new Error('Peso limite');
      return peso * 5;
  }
}

正确用法(Factory + Strategy)

// 业务策略(Strategy Pattern)
const FreteSedex = { calcular: (p) => (p * 10) + 10 };
const FretePAC   = { calcular: (p) => p * 5 };

// Switch 作为路由器(Factory)
const obterEstrategia = (tipo) => {
  switch (tipo) {
    case 'SEDEX': return FreteSedex;
    case 'PAC':   return FretePAC;
    default:      throw new Error('Tipo inválido');
  }
};

// 干净的使用方式
const estrategia = obterEstrategia('SEDEX');
estrategia.calcular(10);

结论

消除 if 本身不应该是目标,而是良好设计的副作用。当决策被恰当地分配——Object Maps 用于静态映射,Guard Clauses 用于校验,显式的业务规则以及 Switch 充当路由器——代码就不再是被动的,而是声明式的。

你并不是在消除决策,而是在消除噪音。这种代码即使在问题上下文已经不再新鲜时,仍然保持可读性。

Back to Blog

相关文章

阅读更多 »

从未运行的 Query 的神秘案件

背景:在过去的周末,我在为我的个人网站进行代码重构时遇到了一个奇怪的 bug。我是一个休闲跑者,我有一个页面列出我所有的 r...

你不讨厌抽象

离周末自由还有一个小时,你正想在逃离去迎接各种刺激计划之前,赶完最后一个工单。