软
软件设计原则
作者:鹿Sir开发工具v1
面向对象设计原则合集:对象健身操九规则、依赖倒置、快速失败错误处理、依恋情节(Feature Envy)检测、揭示意图的命名、类型驱动设计与不可变性优先。在重构代码、设计评审或用户要求改进设计时激活。触发词:设计原则、代码重构、设计评审、对象健身操、依赖倒置、命名规范。
下载量
396
点赞
92
价格
免费
技能文档
--- name: majiayu000-software-design-principles title: 软件设计原则 description: 面向对象设计原则合集:对象健身操九规则、依赖倒置、快速失败错误处理、依恋情节(Feature Envy)检测、揭示意图的命名、类型驱动设计与不可变性优先。在重构代码、设计评审或用户要求改进设计时激活。触发词:设计原则、代码重构、设计评审、对象健身操、依赖倒置、命名规范。 category: 开发工具 --- # 软件设计原则(Software Design Principles) 编写可维护、结构良好代码的专业设计模式与原则。 ## 关键规则 🚨 **快速失败优于静默兜底。** 禁止兜底链(`value ?? backup ?? 'unknown'`)。数据应当存在时,校验并抛出清晰错误。 🚨 **禁用 `any` 与 `as`。** 类型逃逸舱口会击穿类型系统的意义,总有类型安全的解法。 🚨 **让非法状态不可表示。** 用可辨识联合而非可选字段。不该存在的状态组合,让类型系统禁止它。 🚨 **注入依赖,不要实例化。** 方法内部不要 `new SomeService()`,依赖经构造函数传入。 🚨 **只用揭示意图的命名。** 禁用 `data`、`utils`、`helpers`、`handler`、`processor`,按领域职责命名。 🚨 **不写代码注释。** 注释是代码没能表达意图的失败信号——需要注释才能解释时,先重构代码。 🚨 **运行时校验用 Schema。** 外部数据、API 响应、用户输入用 schema 解析校验(如 Zod),从 schema 推导类型,保持类型与校验同步。 ## 适用时机 - 编写新代码(这些是默认标准,不只是重构目标) - 重构既有代码 - 代码评审与设计评审 - TDD 的 REFACTOR 阶段 - 分析耦合与内聚时 ## 核心哲学 设计良好、可维护的代码远比快速完成更重要。每个设计决策都应偏向: - **清晰优于炫技** - **显式优于隐式** - **快速失败优于静默兜底** - **松耦合优于紧集成** - **揭示意图优于泛泛而谈** ## 技能工作流 ### 步骤 1:应用对象健身操九规则 对所有代码严格执行,重构既有代码使其合规: 1. **每方法只允许一层缩进**——提升可读性,迫使抽取辅助方法,更易测试 2. **禁用 ELSE 关键字**——用提前返回代替,减少嵌套,凸显主干路径 3. **包装所有基本类型与字符串**——创建值对象,封装校验逻辑,让领域概念显式化 4. **一等集合**——含集合的类不再包含其他成员,凝聚集合专属操作 5. **每行一个点**——降低耦合,防止依恋情节,遵守迪米特法则 6. **不缩写**——完整、描述性的命名;代码被读的次数远多于写 7. **保持所有实体小**——类 < 150 行、方法 < 10 行、包/模块小而聚焦 8. **类实例变量不超过两个**——高内聚、单一职责、易测试 9. **无 getter/setter/property**——告诉而非询问(Tell, Don't Ask),对象应做事而非暴露数据 **重构时:** 对照九规则逐条审查→识别违规→重构合规→每次修改后验证测试通过。 **评审时:** 检查新代码是否合规→对违规给出重构建议→用领域语言解释理由。 ### 步骤 2:检测依恋情节(Feature Envy) **定义:** 某类中的方法对另一个类的数据或行为的兴趣超过对自己所属类的。 **危险信号示例:** 发票生成器的方法反复调取订单的条目、价格、税率、运费——该方法应该住在订单类里。 **修正:把方法搬去它依恋的类。** 搬迁后发票生成器只做 `new Invoice(order.calculateTotal())`。 **检测协议:** 1. 对每个方法,统计对外部对象的引用次数与对自己对象的引用次数 2. 外部引用 > 自身引用 → 疑似依恋情节 3. 考虑把方法搬到被依恋的类 4. 重跑测试验证行为未变 ### 步骤 3:执行依赖倒置 **原则:** 依赖抽象而非具体实现;不要在方法内直接实例化依赖。 **问题(硬依赖):** 方法内 `new OrderValidator()`、`new EmailService()`——难以测试、难以替换、依赖隐藏、违反单一职责。 **解法(注入):** 依赖经构造函数传入,方法只用不建。 **执行规则:** ```text 禁止:方法内 new 服务对象;调用其他类的静态方法 应当:使用注入的依赖;把创建职责移到构造参数或方法参数 ``` **排查:** 扫描方法体内的 `new`(值对象除外)与跨类静态调用,提取为注入参数,注入后验证测试。 ### 步骤 4:快速失败的错误处理 **原则:** 期望存在的数据缺失时,不要兜底到「有什么用什么」,要带清晰错误快速失败。 **问题(静默失败):** `content.eventType ?? content.className ?? 'Unknown'`——eventType 缺失永远无人知晓,'Unknown' 在系统里扩散,数层之后出错极难排查。 **解法(显式校验):** 缺失即抛错,错误信息包含「期望什么、实际得到什么、调试上下文(如可用键、当前状态)」。 **错误信息格式:** ```text Expected [期望的内容]. Got [实际得到的]. Context: [可用的键、当前状态等调试信息] ``` **应用时机:** 访问「理应存在」的数据时;前置条件必须满足时;不变量必须成立时。 ### 步骤 5:揭示意图的命名 **禁用的泛化命名:** `data`、`utils`、`helpers`、`common`、`shared`、`manager`、`handler`、`processor`——毫无信息量。 **命名检查清单:** | 对象 | 自问 | | ---- | ---- | | 类 | 名字揭示职责了吗?是领域的名词吗?领域专家认得这个词吗? | | 方法 | 名字揭示行为了吗?是动词短语吗?描述业务操作了吗? | | 变量 | 名字揭示内容了吗?对这个上下文足够具体吗? | **重构泛化命名:** 理解真实用途 → 问领域专家会怎么叫 → 提取领域概念 → 全面重命名 → 验证测试。 示例:`getData(id)` → `findCustomerById(customerId)`;`DataProcessor` → `OrderTotalCalculator`。 ### 步骤 6:类型驱动设计 **让非法状态不可表示:** 用类型编码业务规则。订单状态与发货日期用可辨识联合建模,而不是 `status: string` + 可空日期,让「未确认却带发货日期」在编译期就不可能存在。 **禁用类型逃逸舱口**(未经用户明确批准):`any`、`as` 断言、`@ts-ignore` / `@ts-expect-error`。总存在更好的类型安全解法——类型错了就修类型,而不是骗过编译器。 **用类型系统做校验:** 品牌类型(branded type)+ 工厂函数校验,让「已校验的正数」只能以正数身份流转。 ### 步骤 7:默认不可变 **原则:** 默认使用不可变数据。可变状态是意外变更、竞态与难查 bug 的源头。 - 可变写法:函数内直接改入参状态、往入参数组 push——调用方毫不知情 - 不可变写法:返回新对象(展开旧对象 + 变更字段),调用方控制数据流 **规则:** 优先 `const`;优先展开运算符而非修改;优先 `map`/`filter`/`reduce`;必须修改时,显式且收敛。 ### 步骤 8:YAGNI——你不会需要它 **原则:** 需求到来之前不造功能。投机性代码是浪费:写它花时间、维护它花时间,而且等需求清晰时多半是错的。 - 构建当下能用的最简单方案 - 「以后可能需要」不是需求 - 无人使用的代码是维护负担,也是对系统的谎言 - 无情删除投机性代码 ## 想走捷径时的自检 - 想写兜底链(`??` 链)?**停。** 静默兜底隐藏 bug。现在快速失败,好过数层之后调试几小时。 - 想用 `any` 或 `as`?**停。** 你在骗编译器。错误在告诉你类型错了——修类型,不是压症状。 - 想在方法里实例化依赖?**停。** 花 30 秒改到构造函数注入。 - 想命名 `data`/`utils`/`handler`?**停。** 它到底做什么?按领域用途命名。 - 想加 getter 取数据?**停。** 问为什么调用方需要它——对象能不能把事做掉?告诉,别询问。 - 想跳过重构因为「能跑」?**停。** 难改的能跑代码就是技术债,重构是工作的一部分。 - 想写注释解释代码?**停。** 抽函数、改变量名、重排结构——需要解释的代码本身就是问题。 - 想修改入参?**停。** 返回新值,让数据流显式。 - 想造「以后可能用」的东西?**停。** 需求到了再加。 ## 不宜过度应用的场景 - **值对象与 DTO** 可以违反健身操规则(没关系) - **简单脚本** 不需要依赖倒置 - **配置对象** 可以有 getter(它们是数据不是行为) - **测试代码** 对健身操可以放宽 - **第三方库集成代码** 可能需要类型断言 **运用判断力——原则服务代码质量,而不是反过来。** ## 总结检查清单 重构或评审代码时检查: - [ ] 对象健身操:九规则合规吗? - [ ] 依恋情节:方法都在正确的类里吗? - [ ] 依赖:是注入的而非方法内实例化的吗? - [ ] 错误处理:快速失败且错误清晰吗? - [ ] 命名:揭示意图(无 data/utils/helpers)吗? - [ ] 类型:非法状态不可表示吗? - [ ] 类型安全:无 `any`、无 `as` 断言吗?
使用说明
# 软件设计原则 面向对象设计原则合集:对象健身操九规则、依恋情节检测、依赖倒置、快速失败错误处理、揭示意图的命名、类型驱动设计、不可变性与 YAGNI。编写、重构、评审代码时的默认设计标准。 ## 使用场景 - 新代码编写时的设计基线 - 重构遗留代码时的改造依据 - 代码评审/设计评审时的检查清单 ## 用法示例 ```text 按软件设计原则重构这个模块 帮我检查这段代码有没有依恋情节和静默兜底 ``` 内置「想走捷径时的自检」与总结检查清单,注意事项与过度应用的豁免场景。
支持平台:Qoder · QoderWork · Claude · Codex 等 AI 编程助手