关键在于用抽象定义稳定契约,让子类只负责新增行为而不修改父类代码;通过抽象类或接口明确“做什么”,由具体子类实现“怎么做”,配合模板方法模式、里氏替换原则及组合优于继承原则,实现开闭原则。

关键在于用抽象定义稳定契约,让子类只负责新增行为,不碰父类代码。
用抽象类或接口划清扩展边界
父类(或接口)只声明“做什么”,不写“怎么做”。比如定义一个Shape抽象类,只含getArea()抽象方法;具体图形如Circle、Rectangle各自实现。后续加Triangle类,只需新写一个类,Shape和已有图形类都不用改。
- 抽象层越稳定,扩展越安全——避免在抽象类里塞业务判断或默认实现
- 接口适合定义能力契约(如Drawable、Serializable),抽象类适合提供共用骨架(如通用校验、日志模板)
- 粒度要适中:太粗(如一个doEverything())难复用;太细(每个小操作都抽象)反而增加维护成本
模板方法模式是继承支持OCP的典型实践
父类把流程定死,把可变步骤留空(abstract或hook方法),子类只填空不改主干。例如报表生成器:
- 父类ReportGenerator定义generate():先fetchData(),再format(),最后export()
- 其中export()设为abstract,PdfReportGenerator和ExcelReportGenerator分别实现
- 新增CSV导出?加个CsvReportGenerator就行,主流程、其他子类、调用方全不动
子类重写必须遵守里氏替换原则(LSP)
子类不是随便覆盖父类方法,而是延续契约。否则看似扩展了,实则破坏原有逻辑,调用方一换子类就出错。
- 前置条件不能更严:父类方法接受int,子类不能要求必须正数
- 后置条件不能更弱:父类承诺返回非null集合,子类不能返回null
- 异常要兼容:子类不能抛出父类没声明的受检异常
- 语义要一致:父类add()是添加元素,子类不能改成“只记录不真加”
组合与继承配合使用更稳妥
继承适合表达“is-a”且行为差异集中在少数可扩展点;其余变化建议用组合。比如支付模块:
- 用PaymentProcessor接口抽象支付能力(符合OCP)
- OrderService不继承任何支付类,而是持有PaymentProcessor引用(依赖倒置)
- 新增支付方式时,既可写新实现类,也可通过策略模式动态切换,父类完全无感
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











