Community编程与开发github.com

yutna/oo-design

在面向对象的代码库中启动新模块、功能或系统时,用于评估设计规模并选择合适的面向对象设计规则。

oo-design 是什么?

此技能为面向对象代码库提供了一套完整的设计规则和指南。它帮助开发者评估任务的复杂程度(从一次性答案到全新系统),并引导他们遵循一个系统的设计循环:从领域语言表述需求开始,接着确定一致性边界,放置领域逻辑,封装状态与行为,反转依赖关系,最后进行命名和审查。技能还提供了一个症状到技能的查找表,帮助开发者快速识别代码中的问题(如耦合度过高、贫血领域模型、非法状态等)并定位相应的处理规则。它支持Java、Kotlin、TypeScript、JavaScript等多种语言版本,并强调项目自身约定优先于本技能包。

兼容平台✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/yutna/object-oriented-design-skills/tree/HEAD/plugins/core/skills/oo-design

Installed? Explore more 编程与开发 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

面向对象设计

概览

本技能包适用于以面向对象风格编写的代码库的设计规则,并非通用建议。项目自身书写的约定优先于本技能包。遵循正在修改的模块的惯用风格。本技能会评估任务规模并指明适用的技能。

在回答之前加载此索引命名的技能:其规则和示例位于那里,而不在这里。

任务需要多少设计

任务使用跳过
一次性使用后被丢弃的答案什么都不做所有内容
现有类中的一个方法其名称和契约重构
现有流程中的功能该流程的惯用风格新边界
新类或新模块设计循环所有内容
新上下文设计循环,然后确定其边界所有内容
从零开始的新系统设计循环,上下文,然后结构所有内容

设计之前

设计之前,先陈述一条关卡规则:软件必须遵循的一条规则,并提供一个接受示例和一个拒绝示例。例如:“一个预约槽最多只能容纳一个预约。”它接受将第二个病人预约到不同的槽位,拒绝将第二个病人预约到同一个槽位。如果你能写下这条规则,就可以开始。如果不能,那么缺失的就不是设计,本技能包也无法提供。

设计循环

  1. 用领域语言表述软件必须做什么(使用领域语言),并找到了解每个行为所需的对象(分配职责)。
  2. 找出一致性边界:在同一个事务中哪些部分必须同时保持一致(设计聚合)。
  3. 确定每条规则的位置:实体、值对象或领域服务(放置领域逻辑)。
  4. 设计每个对象响应的消息(封装状态和行为),并使非法状态无法构建(使非法状态不可能)。
  5. 反转依赖关系,使领域层对数据库或框架一无所知(反转依赖,分层系统)。
  6. 用领域语言命名内容,并仅注释代码无法表达的内容(命名类和方法,注释代码无法表达的内容)。
  7. 根据以下红旗检查结果(审查对象设计)。

规则的严格程度

本技能包中的每个核心规则都带有三个标签之一,标签说明了它的严格程度。

  • 规则。 违反即为缺陷。任何框架都不能成为借口。
  • 默认。 除非满足指定条件,否则执行此操作。条件自成一句,而非尾随从句。
  • 判断。 两种答案都可以辩护。技能会说明如何权衡。

症状到技能

症状指向处理它们的技能,随着新技能的添加,此表格会增长。

症状技能
哪个类应该做这件事?分配职责
这个类做了太多事情分配职责
一个变更波及许多类诊断耦合
这两个部分之间的耦合有多紧密?诊断耦合
这是一个对象还是仅仅是数据?判断对象或数据
我们的领域模型是贫血的判断对象或数据
其他代码替这个对象做决定封装状态和行为
Setter方法允许此对象违反其规则封装状态和行为
此对象可能以半构建状态存在构建有效对象
此构造函数参数过多构建有效对象
每个新案例都需要编辑工作代码封装变化
这应该有多少灵活性?封装变化
这是一个值对象还是一个实体?建模值和实体
原始字符串和数字承载了领域规则建模值和实体
应该扩展那个类还是持有它?选择组合优于继承
更改基类会破坏其子类选择组合优于继承
子类违反了其父类的承诺尊重可替换性
代码检查它持有的是哪个子类型尊重可替换性
对同一类型的相同 switch 语句反复出现用多态替换条件逻辑
子类还是模式匹配?用多态替换条件逻辑
代码通过一系列对象进行链式调用限制对象的知识范围
这个类只转发调用限制对象的知识范围
这个类或方法应该叫什么?命名类和方法
像 Manager、Helper 或 Util 这样的名称命名类和方法
调用者依赖于它们从不使用的接口方法设计接口
接口还是抽象基类?设计接口
领域代码依赖于数据库或框架反转依赖
对象在哪里创建并组装在一起?反转依赖
调用者知道另一个类如何存储或格式化数据隐藏实现
类泄漏了自己的内部错误或步骤隐藏实现
标志或可选字段允许不可能的组合使非法状态不可能
类型允许领域从未拥有的值使非法状态不可能
代码的用语与团队的不一致使用领域语言
同一个词对两个团队意味着不同的事情使用领域语言
哪些对象必须一起变化?设计聚合
保存冲突或一次加载拉入太多内容设计聚合
允许哪些状态转换?建模生命周期
已取消或已关闭的对象再次变化建模生命周期
领域对象应如何存储和查找?设计仓库
仓库有每个屏幕对应的 find 方法设计仓库
用例重复相同的业务检查放置领域逻辑
领域服务、实体方法还是应用服务?放置领域逻辑
保存变更后其他部分必须做出反应发布领域事件
为回滚的变更发送了消息发布领域事件
一个团队的模型在哪里结束,另一个团队的模型在哪里开始?界定上下文
哪些部分是核心的,哪些可以购买?界定上下文
层间应该以什么方式依赖?分层系统
屏幕或任务在没有其框架的情况下无法测试分层系统
模块以循环方式相互依赖组织模块
模块化单体还是独立服务?组织模块
对 API 的更改破坏了调用它的应用程序跨越系统边界
我们应该如何对此 API 或消息进行版本控制?跨越系统边界
映射工具塑造了我们的领域类驯服持久化
列表对每一行执行一个查询驯服持久化
屏幕需要与模型形状不同的数据分离读写
读取模型、投影还是事件溯源?分离读写
此屏幕使用 MVC、MVP 还是 MVVM?分离表示层与领域层
屏幕直接编辑领域对象分离表示层与领域层
哪种设计模式适合这个问题?选择模式
问题出现之前添加了模式选择模式
这些模式类可以是一个函数语言提供的模式
Visitor,还是带匹配的封闭集合?语言提供的模式
方法在找不到内容时返回 null设计失败行为
拒绝以调用者忽略的代码形式返回设计失败行为
线程共享一个对象并丢失或混合其更改管理共享状态
锁还是此状态的单个所有者?管理共享状态
对另一个系统的调用挂起或中途失败使工作可靠
重试导致工作执行了两次使工作可靠
我们无法根据记录解释事件设计记录内容
我们应该记录、计数或审计什么?设计记录内容
审查发现了上帝类或熔岩流识别反模式
我们应该先修复哪个反模式?识别反模式
即使行为未改变,测试也失败测试面向对象代码
为此协作对象使用 Mock、Stub 还是 Fake?测试面向对象代码
测试依赖于时间或随机值设计可测试性
代码被设为 public 仅仅是为了测试可以访问设计可测试性
我们如何安全地重构这段代码?重构以面向对象设计
重构变得太大而无法完成重构以面向对象设计
我们必须更改没有测试的代码处理遗留代码
逐块替换旧系统处理遗留代码
本次审查应检查什么?审查对象设计
哪些审查发现会导致合并被阻止?审查对象设计
没有人记得代码为什么是这个形状记录设计决策
我们应该绘制什么,以及在什么级别?记录设计决策
注释重复代码或与代码矛盾注释代码无法表达的内容
我们在哪里解释为什么这段代码看起来奇怪?注释代码无法表达的内容
框架钩子或标记持有一条业务规则在框架内工作
我们应该在多大程度上顺应框架?在框架内工作
计算也保存、发送或更改其输入应用函数式技术
哪些函数式技术适合面向对象代码?应用函数式技术

语言技能

语言技能将核心规则保留在一种语言中,并且从不替换它们。基础语言技能适用于没有框架的代码。

  • oo-java,来自 object-oriented-design-java:records、sealed types、equals 和 hashCode、checked exceptions、Optional,以及关闭扩展的类。
  • oo-kotlin,来自 object-oriented-design-kotlin:data classes、sealed types、nullable types、使用 by 的委托,以及扩展函数。
  • oo-typescript,来自 object-oriented-design-typescript:branded identifiers、#private 状态、abstract classes、decorators、discriminated unions,以及 composition root。
  • oo-javascript,来自 object-oriented-design-javascript:c

…

[截断以进行翻译]

相关技能