AgentMarketMCP / SKILL 资产档案馆

目录 / Java 代码简化

SKILL 未评级 已上架

Java 代码简化

--- name: simplify-java-cn description: 在不改变行为的前提下简化 Java / Spring Boot 代码。用于已正常工作的代码清理、减少嵌套、改善命名和消除不必要的复杂度;以职责归属、可替换边界和封装价值辅助判断。覆盖 Java 21 与 Spring Boot 4,不执行版本迁移或全面架构重设计。 metadata: language: zh-CN translation_of: simplify-java version: 0.1.0 --- # Java 代码简化 灵感来自 [Addy Osmani](https://github.com/addyosmani/agent-skills/tree/main/skills/code-simplification)。 ## 概述 减少复杂度,同时精确保留行为。目标不是更少的行数,而是让代码更容易阅读、理解、修改和调试。 每次简化都应回答:新加入项目的同事,能否比以前更快理解这段代码? 已有代码清晰且局部时,保留它。 ## 何时使用 - 功能正常工作,测试通过,但实现比实际需要更复杂。 - 代码审查发现可读性或复杂度问题。 - 出现深层嵌套、冗长方法或含义不清的命名。 - 清理时间压力下完成的实现。 - 整理散落在多处、属于同一职责的逻辑。 - 合并改动后出现重复或风格不一致。 - 用户提供代码片段,要求简化或减少嵌套。 以下情况不适用: - 代码已经清晰,不要为了简化而改动。 - 尚未理解代码行为,先补充上下文。 - 更简单的写法会造成已知且不可接受的性能退化。 - 模块即将整体重写,没有必要清理即将删除的代码。 - 主要问题是功能缺陷、版本迁移或架构重设计,应单独处理。 ## 五项原则 ### 1. 精确保留行为 只改变表达方式,不改变代码做什么。 保持输入、输出、副作用、执行顺序、异常和边界情况。行为还包括调用方依赖的可变性、对象身份、序列化、事务及并发约束。 每次改动前检查: - 对同一输入,是否产生相同结果? - 是否保持相同的失败条件和错误行为? - 是否保持副作用、求值顺序和短路行为? - 现有测试是否无需修改即可通过? 先固定现有行为,再改变结构,最后验证行为没有变化。 不能确定等价时,先获取证据;仍无法确认就保留原写法。不要借简化改变默认值、增加容错或修复业务缺陷。 ### 2. 遵循项目约定 简化应让代码更符合项目,而不是引入个人偏好。 改动前: 1. 阅读适用的 AGENTS.md、CLAUDE.md 和项目约定。 2. 查看相邻代码如何处理类似问题。 3. 确认 Java toolchain、编译 release、Spring Boot 和相关框架版本。 4. 遵循项目已有的领域术语、包结构、导入顺序和命名。 5. 遵循已有的方法组织、异常、null、Optional、Bean 装配和注解约定。 6. 使用项目已有的构建、测试、静态检查和格式化工具。 按项目实际版本选用特性。不在简化任务中升级版本、启用预览特性或更换技术栈。 ### 3. 清晰胜于巧妙 先看数据形状,再看控制流。优先利用已有类型表达合法状态,让分支覆盖和返回结果直接可见。 清晰不等于展开更多语句: - 离散、互斥的已有类型分支,可以采用穷尽 switch。 - 有顺序要求的校验和提前退出,可以采用卫语句。 - 简单的二选一赋值,可以保留三元表达式。 - 复杂条件和计算,可以用准确的名字解释中间含义。 以下示例已有 OrderStatus 枚举。label 是公开入口,约定拒绝 null;状态与标签的映射集中在一个位置。 简化前: ```java import java.util.Objects; public final class StatusLabels { public enum OrderStatus { PENDING, PAID, CANCELLED } public static String label(OrderStatus status) { Objects.requireNonNull(status, "status"); return status == OrderStatus.PENDING ? "Pending" : status == OrderStatus.PAID ? "Paid" : "Cancelled"; } } ``` 简化后: ```java import java.util.Objects; public final class StatusLabels { public enum OrderStatus { PENDING, PAID, CANCELLED } public static String label(OrderStatus status) { Objects.requireNonNull(status, "status"); return switch (status) { case PENDING -> "Pending"; case PAID -> "Paid"; case CANCELLED -> "Cancelled"; }; } } ``` 三种现有状态的结果保持不变,null 仍抛出消息为 "status" 的 NullPointerException。 每种状态现在都明确对应结果。以后新增枚举常量时,重新编译会要求补充分支,避免新状态静默落入“Cancelled”。 此例不要求把现有布尔参数改成枚举。数据模型或调用契约需要变化时,应单独评估,不混入局部简化。 ### 4. 保持平衡 警惕过度简化: - 过度内联可能删除表达业务概念的名字。 - 合并无关逻辑可能让两个简单方法变成一个复杂方法。 - 简短包装层可能承担测试、替换或框架拦截职责。 - 更少的代码行不一定意味着更低的理解成本。 只有涉及提取、合并或删除边界时,再使用以下判断: - **职责归属:** 状态和维护其不变量的规则留在同一责任范围。同一业务决策尽量集中,不因代码相似而合并不同业务目的。 - **seam:** 识别能够替换依赖而不必修改使用方逻辑的位置。已有注入点可能已经足够,不要求再增加 Java interface。 - **深度模块:** 用简单的调用契约封装复杂实现,让调用方少知道内部细节。模块可以是方法、类或包。 - **完整契约:** 除方法签名外,还要考虑前提、不变量、调用顺序、失败方式和必要配置。 - **删除检验:** 删除包装层后,复杂度若只是消失,可以考虑删除;若细节扩散到多个调用方,封装仍有价值。同时检查框架行为和实际替换用途。 - **局部性:** 尽量让同一规则的实现、变更和验证集中,减少理解一个行为所需的跨文件跳转。 - **当前用途:** 先减少复杂度,再考虑增加结构。只有当前范围内的真实变化、替换或测试需求能够证明价值时,才增加抽象。 - **契约测试:** 从现有调用契约验证行为,不为测试私有步骤而扩大公开接口。 检查已有的数据入口和内部不变量。外部数据应在入口解析和校验,内部使用已建立的契约;不能仅凭这一原则删除现有校验,必须先证明它确实重复且不会改变错误行为。 已有概念与同步契约时,保留职责归属和协作规则。不把普通 Spring 应用迁移为概念与同步架构。 如果改善需要重新设计公开契约或跨概念转移职责,单独说明,不在本次简化中展开。 ### 5. 限定改动范围 优先处理用户指定的代码;未指定时,默认处理近期改动。 选择能够解决当前可读性问题的最小改动。不要顺手重构无关代码。 只为理解和验证当前改动读取必要的关联上下文,不扩展成全库架构扫描。 ## 简化流程 ### 步骤一:理解后再动手 删除结构之前,先理解它为什么存在。 回答: - 代码承担什么职责?操作什么数据? - 哪些输入合法?数据在哪里解析和校验? - 谁调用它?它调用谁? - 边界情况和错误路径是什么? - 哪些测试定义了当前行为? - 方法、包装层或注解是否承担框架运行时职责? - 结构是否源于性能、平台限制或历史兼容需求? - 必要时,git blame 和提交记录能否解释原因? 代码片段缺少仓库上下文时,说明可确认的范围,不虚构项目约定或验证结果。 ### 步骤二:识别具体机会 以下是检查信号,不是必须修改的阈值。 | 模式 | 信号 | 可考虑的简化 | | --- | --- | --- | | 深层嵌套 | 多层控制流难以追踪 | 卫语句或具名辅助方法 | | 长方法 | 包含多个独立意图 | 按完整意图拆分,不按行数切碎 | | 嵌套三元表达式 | 分支覆盖或优先级难以看清 | if/else 或适合的 switch | | 布尔参数串 | 调用意图不明确 | 检查数据形状;只在契约允许时采用具名方法或已有类型 | | 重复条件 | 同一业务判断散落多处 | 在同一职责内提取具名谓词 | | 泛化或误导命名 | 名字不能解释内容或副作用 | 使用项目领域语言 | | 解释“做什么”的注释 | 与清晰代码重复 | 删除冗余注释 | | 解释“为什么”的注释 | 记录约束或取舍 | 保留 | | 重复逻辑 | 同一规则存在多个实现 | 确认职责相同后提取 | | 死代码 | 不可达分支、未用变量、旧注释代码 | 确认框架、反射和配置不再使用后删除 | | 无价值包装 | 仅转发且不承载契约或运行时行为 | 内联或直接调用 | | 过度设计 | 多层工厂或没有实际用途的策略结构 | 用满足当前需求的直接表达替代 | | 冗余类型转换 | 看似可以省略转换 | 确认重载选择、运行时检查和行为不变后删除 | 不要用默认分支、强制转换或警告抑制掩盖类型不匹配。若问题来自数据模型,指出它,不在局部简化中偷偷重设计。 ### 步骤三:逐步修改 一次完成一个可独立理解和验证的简化。 1. 运行现有相关测试,确认基线。 2. 必要时补充刻画现有行为的测试,在原实现上确认结果。 3. 完成最小结构改动。 4. 运行相关测试和检查。 5. 通过后继续下一项;失败时撤回本次改动或重新判断原因。 测试从调用方可观察的输入、结果、副作用和错误出发,不锁定私有方法拆分或内部调用形式。 保行为重构的测试通常应在原实现上通过。不要为了制造“先红后绿”而改写预期或故意破坏生产代码。需要检查测试是否有效时,可在隔离环境使用受控错误实现或变异验证。 无法诚实测试时,报告缺口,不写只检查代码形状的测试来充数。 将简化与功能新增、缺陷修复分开,保持差异可审阅。仅在获得授权时提交或发布。 若重构涉及超过 500 行,优先评估 IDE 重构、AST 工具或其他可预览的自动化。规模大不意味着可以扩大任务范围,也不意味着可以省略逐步验证。 ### 步骤四:验证整体结果 比较改动前后: - 是否更容易理解? - 是否引入了不符合项目习惯的新模式? - 同一决策是否仍集中在一个位置? - 调用方是否需要了解更多实现细节? - 差异是否集中、清晰、可审阅? - 同事是否会认为这是净改善? 如果结果更难理解或审阅,撤回简化。 简要记录和报告改了什么、为何更清晰、验证了什么,以及仍有哪些不确定项。 ## 语言与框架指导 ### Java 21 优先考虑能够减少重复判断、转换或样板的稳定特性,以理解成本和行为等价决定是否采用。 - **模式匹配 switch:** 可简化重复的类型判断和转换。保留 null 处理、分支优先级、守卫条件、兜底结果和异常。不为穷尽匹配而重新设计类型层次。 - **Record Patterns:** 可简化对已有 record 的解构。检查嵌套 null,以及访问器调用次数、顺序和异常。解构比具名访问更难读时,保留原写法。 - **record 数据载体:** 可用于真正具有值语义的数据类型。转换前确认可变性、相等性、构造校验、公开访问方式和序列化兼容;不机械转换实体、继承型对象或代理 Bean。 - **Sequenced Collections:** 可用首尾访问和反向遍历 API 表达意图。检查空集合异常、顺序、可变性和别名关系;reversed() 视图不能替代原先的独立副本。 - **Stream 与 Optional:** 不机械替换循环和条件判断。保留求值时机、副作用、顺序及返回集合的可变性;orElse 与 orElseGet 不自动等价。 - **虚拟线程:** 理解并保留现有执行模型,不自动替换线程池、异步链或响应式链。涉及并发上限、超时、取消、上下文传播和资源生命周期的变化应单独处理。 不要为减少参数或局部变量而引入共享可变状态。 ### Spring Boot 4 简短方法和声明式配置也可能承载重要行为。 - **代理与事务:** 删除或移动方法前,检查注解、调用路径和代理拦截。保留事务边界、回滚、鉴权、校验、缓存与异步行为;经过代理的调用与类内自调用不自动等价。 - **HTTP 与持久化:** 保留状态码、响应字段、异常映射、校验顺序、持久化副作用、懒加载访问和有意义的查询顺序。 - **HTTP Service Clients:** 已采用声明式客户端时,检查注册和分组配置能否替代重复装配。保留请求、鉴权、超时、错误映射、序列化与观测配置。声明式接口可以承载真实的远程调用契约;不因没有手写实现就删除它,也不为统一风格迁移全部客户端。 - **JSpecify:** 遵循项目已有的空值约定,区分容器可空和元素可空。非空注解不能替代运行时校验;只有实现及调用路径能证明冗余时,才删除 null 检查。 - **Jackson:** 确认实际使用的版本、mapper 和模块。只有自动配置与当前配置等价时,才删除重复构建或注册代码。验证字段、日期、枚举和空值行为,不混入 Jackson 版本迁移。 - **模块与自动配置:** 删除依赖或配置前,检查对应模块、条件装配、运行时发现和测试支持。没有显式 import 不代表没有用途。 - **框架验证:** 触及运行时边界时,使用已有集成测试或针对性行为测试验证。普通单元测试可能绕过代理和自动配置;不要通过扩大测试上下文掩盖装配问题。 ## 常见借口 | 借口 | 判断 | | --- | --- | | 能运行就不用改 | 当前范围内难以理解的代码值得简化,但清晰代码不必改 | | 行数越少越简单 | 比较理解成本,不比较行数 | | 顺手把旁边也整理一下 | 无关改动增加审阅噪声和回归风险 | | 类型已经说明一切 | 类型不能完整表达副作用和运行时调用约束 | | 这个抽象以后可能有用 | 用当前用途证明价值,不为假想需求保留复杂度 | | 原作者一定有理由 | 先查原因;历史原因可能存在,也可能已经失效 | | 使用新特性一定更现代 | 只有清晰度改善且行为等价,才构成简化 | | 多加几个检查更安全 | 在真实边界建立契约;重复防御可能掩盖错误的数据流 | | 加功能时一起重构更省事 | 分开变更,便于审阅、验证和回退 | ## 危险信号 - 必须修改测试预期,才能让简化通过。 - 代码更短,但理解需要更多跳转或框架知识。 - 为符合个人喜好而重命名。 - 删除错误处理、入口校验或框架注解来换取整洁。 - 用新的默认值掩盖原来的异常或非法状态。 - 为分层而增加接口、适配器和转发层。 - 未理解代码就删除包装或配置。 - 为测试内部实现而扩大公开接口。 - 把执行模型、依赖升级或公开契约变化混入清理。 - 一次进行大量未经独立验证的简化。 - 未经要求修改任务范围之外的代码。 ## 验证 - [ ] 现有测试无需修改即可通过。 - [ ] 必要的行为测试在原实现和修改后实现上均通过。 - [ ] 使用项目实际 Java release 构建成功,无新增警告。 - [ ] 项目已有静态检查和格式化检查通过。 - [ ] 每项简化可独立理解和审阅。 - [ ] 差异中没有无关改动。 - [ ] 命名、结构和用法符合项目约定。 - [ ] 错误处理和必要的运行时校验没有被删除或削弱。 - [ ] 涉及的 null、异常、集合、求值和序列化语义保持不变。 - [ ] 涉及的代理、事务、自动配置与客户端边界已验证。 - [ ] 没有遗留未使用的导入、变量或不可达分支。 - [ ] 简化没有把内部复杂度转嫁给调用方。 - [ ] 未能执行的检查和无法确认的行为已明确报告。 只勾选实际完成的检查。测试通过是证据,不是所有行为等价的证明。

模型生成摘要(rules/v1 · 2026-11-14 16:21):---
这是模型对公开材料的总结,不是官方声明,请以原链内容为准。

存档时间线

版本存档时间内容哈希内容
v12026-11-14 16:205b14ac13 可取

版本索引永久保留;内容副本只保留最新 1 份,更早版本仅留索引与哈希(存档时间线的证据链不会因此断裂)。

下载存档内容副本

纠错与举报(发现条目失效、署名有误或涉及侵权?)
提交举报 / 纠错

侵权举报经核验成立后,我们会即时下线该条目并删除已存的内容副本。