清晰的继承结构应按业务本质划分层级,父类只包含稳定通用的共性特征,避免功能堆砌;使用抽象类明确契约,控制继承深度不超过三层,命名需直白体现职责。

关键在于让继承关系真实反映业务语义,而不是为了复用而强行拉关系。可读性差的继承树,往往源于“父类塞太多”“子类承担不该有的职责”或“层级过深”。真正清晰的继承结构,一眼就能看出“谁是什么、能做什么、为什么这么分”。
按业务本质划分层级,拒绝功能堆砌
父类只放稳定、通用、不可变的共性特征,比如“有身份标识”“可登录”“具备基础状态管理”。不要把某个子类将来可能用到的功能提前塞进父类。
- 错误做法:在User父类里加sendEmail()、generateReport()、exportData()——这些其实是不同角色的专属行为
- 正确做法:定义Person(含name/id)→ Staff(含employeeId/department)→ Teacher(加teach()、assignHomework())和Admin(加manageUsers()、auditLog())
- 判断标准:如果删掉某个方法,会不会导致其他子类逻辑崩溃?如果不会,它就不该在父类
用抽象类明确契约,避免“空壳父类”
纯抽象父类(含abstract方法)比普通类更能传达设计意图。它强制子类思考“我必须实现什么”,而不是“我能用父类的什么”。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 例如:PaymentProcessor作为抽象类,声明process()和validate()为abstract,子类AlipayProcessor、WechatProcessor各自实现细节
- 好处:阅读代码时,看到new AlipayProcessor(),立刻知道它必然支持支付和校验;看到调用processor.process(),无需翻源码就知道这是个标准流程入口
- 避免写一个叫BaseService的普通类,里面全是空方法或默认返回null——这会让读者反复怀疑“这个方法到底该不该重写?”
控制深度,优先横向扩展而非纵向拉长
继承链超过三层(A → B → C → D)就会显著增加理解成本。深层继承意味着每次看子类都要向上跳转多次,容易迷失上下文。
- 常见问题:Animal → Mammal → Carnivore → Dog → GermanShepherd → PoliceDog……越往后越难概括共性
- 替代方案:把“警用能力”抽成接口Trainable或Deployable,让PoliceDog同时实现Dog和Deployable,而不是硬接在Dog下面再开一条支路
- 经验法则:同一层子类数量 ≥ 3 且语义清晰时,说明当前抽象合理;若某层只有1–2个子类,大概率是过度拆分或抽象不足
命名直白,让类名本身成为文档
类名不解释其用途,就是设计失败。继承树中的每个节点都应像自然语言一样可读。
- 好名字:EditableDocument、VersionedResource、AuditableAction——看到就懂约束和能力
- 坏名字:BaseManager、CommonUtil、SuperBean——完全无法推断职责边界
- 技巧:把类名代入句子检验:“一个Invoice是一个FinancialDocument”成立;“一个Invoice是一个BaseObject”毫无信息量
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










