过度设计的典型信号是脱离实际需求的复杂架构,如UserService依赖7层接口却只支持注册登录;应坚持最小契约、直命名、删冗余抽象、慎用设计模式,并以三行红线控制重构成本。

过度设计的典型信号:从代码里闻出“腐味”
当你发现一个 UserService 依赖了 7 层接口、3 个抽象工厂、2 套策略上下文,而实际业务只支持「注册」和「登录」两个操作——这就是过度设计。Java OOP 的封装、继承、多态本为解耦服务,但一旦脱离具体场景,就会变成维护负担。
常见腐味包括:
• 接口定义远超当前需求(如 IUserOperation 包含 archiveByBatchWithRetry(),但系统从未批量归档)
• 抽象类里塞满 protected abstract 方法,子类却只重写其中 1 个
• Spring 配置中大量 @ConditionalOnMissingBean 堆叠,只为预留“未来可能有的第三方实现”
用“最小契约”代替“最大抽象”
与其一上来就画 UML 类图,不如先写测试用例或接口文档。比如用户导出功能,先明确输入是 List、输出是 byte[]、格式是 CSV——这就够了。
实操建议:
• 接口命名直指用途,如 CsvExporter,而非 DataExportService
• 抽象类只提取真正复用的逻辑(如通用 Excel 表头生成),不为“看起来整齐”而抽象
• 删除所有带 Base、Abstract 前缀但仅被一个子类实现的类
• Spring Bean 尽量用 @Service 直接声明,避免套一层 DefaultXxxImpl 再加接口
立即学习“Java免费学习笔记(深入)”;
警惕设计模式的“自动补全”冲动
观察团队代码,常出现「看到 if-else 就想上策略模式」「遇到 new 对象就反射+配置化」「有状态切换立刻建状态机」——这些不是错,而是过早引入复杂度。
判断依据:
• 策略模式:当算法分支 ≥ 3 种且预计会动态增减(如支付渠道从微信/支付宝扩展到 Apple Pay、PayPal)才值得
• 工厂模式:当构造逻辑涉及多步校验、资源获取(如创建带连接池的 HttpClient),而非简单 new User()
• 模板方法:父类中真正存在 >2 行可复用的骨架代码,且子类差异仅在少数钩子方法
public class CsvExporter {
// ✅ 直接实现,无接口、无抽象、无策略
public byte[] export(List users) {
StringBuilder csv = new StringBuilder();
csv.append("id,name,email\n");
for (UserDTO u : users) {
csv.append(u.getId()).append(",").append(u.getName()).append(",").append(u.getEmail()).append("\n");
}
return csv.toString().getBytes(StandardCharsets.UTF_8);
}
}
重构比预防更可靠,但得守住“三行红线”
没人能一开始写出刚好够用的设计。关键是让重构成本可控:任何新增需求,如果需要修改超过 3 个类、或改动现有接口签名、或导致 >5 个测试失败——说明原有设计已僵化,该动刀了。
落地守则:
• 所有公共方法必须有单元测试覆盖核心路径(哪怕只有 1 个 case)
• 接口变更前,先用 @Deprecated 标记旧方法,给调用方缓冲期
• 删除抽象层时,IDE 用 “Safe Delete” 而非手动删,确保没漏掉隐式依赖
• Spring Boot 项目中,优先用 @ConfigurationProperties 替代自定义 FactoryBean
最常被忽略的一点:过度设计往往不是技术能力问题,而是对需求不确定性的焦虑投射。把“未来可能要支持国际化”写成接口,不如先在 messages_zh.properties 里加一行真实文案。代码跑起来,需求才会显形。










