跳到正文
教程31 篇

架构与代码质量第 1 / 3 篇

避免越写越乱:先隔离会一起变化的规则

用价格计算的小例子区分业务规则、存储和界面,不为未来先造框架。

进阶约 25 分钟练习资料核实

学习目标

一个能独立检查的规则函数,以及清楚的输入、输出和调用边界。

适用场景

虚构预约页面同时做优惠计算、读取数据库、发送邮件和显示错误。每改一种价格规则都得动界面、接口和通知代码。练习代码是示意,不是可直接上线的计费模块。

准备条件

  • 一个确实反复修改或难以验证的函数;单凭文件行数不启动重构。
  • 已确认的当前业务规则与原行为用例,不用模型猜优惠和税费。

操作步骤

  1. 沿一次真实修改寻找混杂职责

    选最近一次需求,例如“新增一种优惠”。圈出为了同一规则一起变化的地方,再标出与规则无关的 I/O。先找到重复知识和修改牵连,不按“每个文件不得超过多少行”机械拆分。

    变更记录,而不是目录美化
    规则:本练习仅有固定金额优惠,结果最低为零。
    规则出现处:服务端报价函数、邮件模板里的另一次计算。
    I/O:查价、存订单、发邮件。
    候选:报价规则集中一处,邮件只展示已确认报价。
    不做:通用插件系统、微服务、五种策略类。
    未知:真实项目的币种、退款、税费,应另行确认。
  2. 定义一个小而完整的规则接口

    让规则函数只接收已经取得的数据,返回计算结果;由调用者处理存储与通知。边界应减少调用者必须知道的事情,而不是把原大函数拆成一堆依赖全局状态的小函数。

    最小 TypeScript 示意
    type QuoteInput = { subtotalCents: number; discountCents: number };
    export function quote({ subtotalCents, discountCents }: QuoteInput): number {
      if (![subtotalCents, discountCents].every(Number.isSafeInteger) ||
          subtotalCents < 0 || discountCents < 0) {
        throw new Error("Amounts must be non-negative safe integer cents");
      }
      return Math.max(0, subtotalCents - discountCents);
    }
    // 外部负责取得输入、存储结果;此函数不读数据库、不发送通知。
  3. 先验证已确认的边界例子

    用正常、零值、优惠超额和非法输入检查规则。不要给示意代码补编真实财务规则;如果价格需要多币种或小数精度,重新明确契约。保存报价后,通知应展示同一份结果,避免不同出口重新计算出差异。

    示例输入与预期
    subtotalCents=1000, discountCents=200 => 800
    subtotalCents=0, discountCents=0 => 0
    subtotalCents=1000, discountCents=1200 => 0
    discountCents=-1 或 1.5 => 拒绝
    存储或通知失败 => 由调用流程处理,不改写报价规则
  4. 用下一次修改判断边界是否值得保留

    模拟再调整同一条真实规则,记录需要理解和修改哪些地方。若只多了文件跳转、调用者仍要拼装全部细节,就撤回无益的拆分。架构好坏看改变是否更安全、更易理解,不看模式名称和层数。

常见误区

复制三份相似代码就马上写通用引擎。
先确认是否是同一业务知识;仅外观相似、变化原因不同,可以保持独立。
把数据库错误悄悄转换成报价为零。
区分规则结果与外部失败,向调用者保留可处理的错误语义。

验收清单

  • 拆分来自实际修改牵连,而非行数阈值。
  • 规则输入输出、单位和非法值处理明确。
  • 不同出口没有各自复制一套报价规则。
  • 同一修改场景下,调用者需要了解的细节确实减少。

资料与版本

本文为原创方法整理,示例与提示词需按项目调整;不代表真实用户研究或账号执行结果。