用 Object Maps 将环形复杂度换成 O(1)

发布: (2025年12月14日 GMT+8 09:49)
5 min read
原文: Dev.to

Source: Dev.to

介绍

你是否有过每次需要修改复杂业务规则(尤其是 UI)时像踩在鸡蛋上那种感觉?无论是处理订单状态、支付网关还是权限等级,保持代码的一致性常常像在雷区里行走。

很多开发者的自然反应(这让我想起了自己的入门阶段 :D)是把这些变体写进 switch 或者一长串 if/else。问题是只要忘记了一个 case 或者链式写错了,立刻就会出现一个在生产环境中悄然运行的 bug。

很多人会尝试用 Guard Clauses(著名的 “提前返回”)来解决。这是一种优秀的技巧,能够清理验证逻辑并避免过度嵌套。顺便说一句,这本身就是一个值得单独写篇文章的主题。

但今天的目标更进一步。我不只是想整理 if,而是要消除它们。目标是停止编写防御性条件,转而写出意图。

如何在实践中应用

当我们面对可预见的规则时,Object Maps 配合 Mapped Types 提供了一种更为稳健的方案。关键在于把条件判断的脆弱性转化为合约的安全性。

真实案例(React Native + Zustand)

想象一个外卖应用,每个订单状态都对应:显示文字、颜色、是否可取消以及点击时的行为。

定义域

export type OrderStatus =
  | 'pending'
  | 'confirmed'
  | 'preparing'
  | 'delivered'
  | 'cancelled';

type OrderStatusConfig = {
  label: string;
  canCancel: boolean;
  // Note que tirei a cor daqui. Já explico o porquê! 👇
};

Mapped Type 以确保完整覆盖

// O compilador obriga que TODAS as chaves de OrderStatus existam aqui.
type OrderStatusMap = {
  [K in OrderStatus]: OrderStatusConfig;
};

export const orderStatusMap: OrderStatusMap = {
  pending:   { label: 'orderStatus.pending',   canCancel: true },
  confirmed:{ label: 'orderStatus.confirmed', canCancel: true },
  preparing:{ label: 'orderStatus.preparing', canCancel: false },
  delivered:{ label: 'orderStatus.delivered', canCancel: false },
  cancelled:{ label: 'orderStatus.cancelled', canCancel: false },
};

如果你新增了一个状态却忘记映射,TypeScript 会直接报错,阻止构建。

更实用的方式

如果想要更精确的类型推断且不失检查,satisfies 操作符非常合适。它会验证对象是否符合合约,同时保留字面量类型。

export const orderStatusMap = {
  pending:   { label: 'orderStatus.pending',   canCancel: true },
  confirmed:{ label: 'orderStatus.confirmed', canCancel: true },
  preparing:{ label: 'orderStatus.preparing', canCancel: false },
  delivered:{ label: 'orderStatus.delivered', canCancel: false },
  cancelled:{ label: 'orderStatus.cancelled', canCancel: false },
} satisfies Record;

职责分离(Domain 与 UI)

规则映射 (orderStatusMap) 并不需要了解你的设计系统。我们可以再创建一个仅负责把状态映射到主题的映射表:

// Vinculando chaves do status às chaves do seu tema
// (Ex: NativeBase, Restyle, etc)
export const orderStatusColorMap = {
  pending:   'warning',
  confirmed:'info',
  preparing:'orange',
  delivered:'success',
  cancelled:'danger',
} satisfies Record;

在 UI 中使用(零 ifs)

使用 Zustand 的 Store

import { create } from 'zustand';

type OrderStore = {
  status: OrderStatus;
  setStatus: (status: OrderStatus) => void;
};

export const useOrderStore = create(set => ({
  status: 'pending',
  setStatus: status => set({ status }),
}));

React Native 组件

import { useTranslation } from 'react-i18next';
import { useTheme } from '@react-navigation/native';
import { Pressable, Text } from 'react-native';
import { orderStatusMap, orderStatusColorMap } from './maps';
import { useOrderStore } from './store';

export function OrderStatusView() {
  const { t } = useTranslation();
  const status = useOrderStore(state => state.status);
  const theme = useTheme();

  // Desestruturação direta da regra de negócio
  const { label, canCancel } = orderStatusMap[status];

  // Resolução da cor via Design System
  const color = theme.colors[orderStatusColorMap[status]];

  return (
    <>
      {t(label)}

      {canCancel && (
        console.log('Cancelar')
      )}
    </>
  );
}

更进一步

这种模式并非只适用于 UI。想象一下处理不同网关(Stripe、Mercado Pago)下的 PIX 与信用卡支付时,使用 Object Map 返回对应的执行策略:

export const paymentStrategies = {
  pix: async (data, gateway) => {
    return adaptersMap[gateway].pix(data);
  },
  credit_card: async (data, gateway) => {
    return adaptersMap[gateway].creditCard(data);
  },
} satisfies Record;

// Uso na aplicação:
const result = await paymentStrategies[method](negotiation, selectedGateway);

为什么这改变了局面?

  • 复杂度 O(1) – 消除了阅读多个 else 带来的认知成本。
  • 安心 – 当我新增状态或支付方式时,无需去寻找所有 if 的位置,编译器会指明错误所在。
  • 合约清晰 – 代码不再是防御式的,而是表达意图的。

那么,你平时会使用 Object Maps 吗?还是仍然依赖记忆去维护 switch case 的完整性? 👇

Back to Blog

相关文章

阅读更多 »

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

引言 我已经谈过 Object Maps,这是一种强大的技术,用于替代 switch 或 if/else 链。使用它,我们用访问来取代 cyclomatic complexity……

JavaScript 中的一等函数

介绍 对于学习 JavaScript 的开发者来说,术语 first‑class functions 在讨论和文档中经常出现。在 JavaScript 中,函数 a...