抽象类应聚焦业务本质中长期不变的部分,定义稳定契约而非堆砌功能;需控制继承深度三层封顶,配合接口与组合替代深层继承,确保结构松而不散、紧而不僵。

抽象类在大型项目中控制继承层次,核心是“用得准、压得稳、分得清”——它不是用来堆功能的中转站,而是定义稳定契约的锚点。
只放不可变的共性,不塞将来可能用的功能
父级抽象类应聚焦业务本质中长期不变的部分,比如身份标识、基础状态机、统一日志入口。一旦某个方法删掉后不影响其他子类运行,它就不该出现在抽象类里。
- 错误示例:在BaseUser里提前加入
sendNotification()、exportToExcel()、syncWithLDAP()——这些是角色专属行为,不是所有用户都具备 - 正确做法:定义Person(含id/name)→ AccountHolder(含accountNo/balance)→ 再分出Customer和Staff,各自实现业务动作
用抽象方法强制契约,避免空壳父类
纯抽象方法比默认空实现更能传递设计意图。看到abstract void process();,开发者立刻明白这是必须落地的关键环节,而不是猜“这个方法要不要重写”。
- 推荐结构:以PaymentProcessor为抽象类,声明
abstract boolean validate();和abstract Result execute(); - 拒绝写法:一个叫BaseService的类,里面全是
public void doX() {}或return null;——这会让阅读者反复怀疑设计本意
限制层级深度,三层封顶
超过三层(A → B → C → D)的继承链会显著增加理解成本。每次看子类都要向上跳转多次,容易丢失上下文。真正可维护的结构,往往靠横向拆解,而不是纵向拉长。
立即学习“Java免费学习笔记(深入)”;
- 常见陷阱:Order → OnlineOrder → InternationalOrder → ExpressInternationalOrder → PriorityExpressInternationalOrder
- 优化方向:把“跨境”“加急”“优先”抽成接口(CrossBorderCapable、ExpressDispatchable),让具体订单类按需实现,而非硬接在继承树末端
- 经验判断:同一层有3个及以上语义清晰的子类,说明当前抽象合理;若某层只有1–2个子类,大概率是抽象粒度失当
配合接口与组合,替代深层继承
抽象类负责“是什么”,接口负责“能做什么”,组合负责“由什么组成”。三者协同,才能让结构松而不散、紧而不僵。
- 例如:一个ReportGenerator抽象类定义
generate()和format()骨架,但导出渠道(PDF/Excel/API)用Exporter接口隔离 - 再如:不把“缓存能力”塞进父类,而是让具体服务持有CacheClient实例,通过组合注入,既解耦又易测
- 好处:新增一种导出方式或缓存实现时,无需改动继承体系,只需新增实现类并注入即可


















