松耦合系统应优先用接口定义能力契约、抽象类封装通用流程:接口声明行为边界(如ILoggable),抽象类提供模板方法(如ReportGenerator),JDK 8+中接口default方法仅用于无状态逻辑,复杂逻辑仍属抽象类。

松耦合系统的关键在于让模块之间只依赖约定,不依赖具体实现。抽象类和接口正是实现这一点的两种不同路径:抽象类适合表达“是什么”的共性结构,接口适合定义“能做什么”的能力契约。
用接口定义行为边界,隔离变化点
当多个模块需要协作但又不想彼此牵扯时,优先用接口。比如日志模块和业务模块之间,不直接调用Log4j或SLF4J的具体类,而是定义一个ILoggable接口:
- 接口只声明getLogId()、logError(String msg)等方法,不暴露任何实现细节
- 业务类只持有ILoggable类型的引用,运行时才注入具体实现(如FileLogger或CloudLogger)
- 后续替换日志框架时,只需新增实现类,业务代码零修改
用抽象类封装通用流程,避免重复逻辑
当一组子类共享大量相同逻辑,仅少数步骤差异化时,抽象类更合适。典型如报表生成系统:
在调试 Java 后端 API 故障时使用此技能,尤其适用于以下任务:分析日志、跨 Controller、Service 和 Mapper 层追踪请求、定位异常根因、检查 SQL 执行或参数流转,以及生成简洁的诊断结论与修复方案。
- 定义ReportGenerator抽象类,包含generateReport()模板方法,内部调用readData()、formatData()、exportReport()
- 其中exportReport()是已实现的公共逻辑(统一压缩、加水印),而前两个方法声明为abstract
- ExcelReportGenerator和PdfReportGenerator各自实现读取和格式化逻辑,复用父类骨架
组合使用:接口定义能力,抽象类提供默认实现
现代Java(JDK 8+)支持接口中添加default方法,这模糊了二者界限,但设计意图仍应清晰:
立即学习“Java免费学习笔记(深入)”;
- 接口仍以定义契约为主,default方法仅用于极简、无状态的通用逻辑(如空值校验、简单转换)
- 涉及状态管理、复杂初始化或需构造器参数的逻辑,必须放在抽象类里(接口不能有构造方法)
- 例如:PaymentProcessor接口声明process()和refund();AbstractCreditCardProcessor抽象类封装卡号加密、风控调用等共用流程
规避常见设计陷阱
错误选择会悄悄增加维护成本:
- 把本该是能力契约的场景写成抽象类(如Flyable做成抽象类),导致子类被迫继承无关字段或方法,违反单一职责
- 在抽象类中塞入大量final或static方法,限制子类扩展自由度
- 接口方法签名设计过宽(如一个接口含15个方法),迫使实现类重写大量空方法——应拆分为多个小接口(如Readable、Writable、Searchable)

















