软件设计原则

作者:鹿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
按软件设计原则重构这个模块
帮我检查这段代码有没有依恋情节和静默兜底
```

内置「想走捷径时的自检」与总结检查清单,注意事项与过度应用的豁免场景。

如何安装此技能?

访问技能市场,点击「安装」按钮,按提示将技能包放入 AI 编程助手的 skills 目录即可。

浏览技能市场

支持平台:Qoder · QoderWork · Claude · Codex 等 AI 编程助手

软件设计原则 - 免费 | 技能派