抽象类不推荐作为模块间解耦的主要手段,更适合模块内部代码复用和模板统一;模块间解耦应依赖无状态、多实现自由的接口,因其不携带实现细节、初始化逻辑或状态字段。

抽象类在模块解耦中作用有限,**不推荐作为模块间解耦的主要手段**。它更适合模块内部的代码复用和模板统一,而非跨模块通信。真正承担模块解耦职责的,是接口。
为什么抽象类不适合模块间解耦
抽象类天然带有实现倾向——它可以包含字段、构造器、非抽象方法甚至静态代码块。一旦模块A依赖某个抽象类,它就可能无意中耦合到:
- 该抽象类的初始化逻辑(比如构造参数、static块)
- 共用状态字段(如protected int retryCount),导致行为不可预测
- 模板方法中的固定流程(如beforeProcess() → doWork() → afterProcess()),限制了下游模块的自由度
- 单继承限制:模块B若已继承其他基类,就无法再继承该抽象类
抽象类的合理定位:模块内骨架,不是模块间契约
它应在**同一模块内部**用于:
- 抽取多个具体类的公共逻辑(如统一的日志前缀、基础校验、资源释放模板)
- 强制子类实现关键步骤(用abstract方法定义扩展点)
- 避免重复代码,但不暴露给其他模块调用
例如,支付模块内可有 AbstractPaymentProcessor 封装通用幂等处理和异步回调包装,但它只被本模块下的 WechatPaymentProcessor 和 AlipayPaymentProcessor 继承,不对外导出。
模块间通信必须用接口
跨模块协作应严格依赖接口,因为接口只承诺“能做什么”,不携带任何实现细节或状态:
- 无状态:没有字段、没有构造器、没有初始化顺序问题
- 多实现自由:一个模块可同时提供多个接口实现,另一模块按需注入
-
框架友好:Spring 的
@Autowired按接口类型装配更稳定,不会因抽象类构造异常而启动失败 - 测试友好:Mockito 可轻松 mock 接口,但难以干净地 mock 抽象类(尤其含 final 方法或复杂初始化时)
如果非要跨模块用抽象类?请立刻转成接口
常见误区:为“统一基类”把 BaseService 抽成抽象类并让多个模块继承。正确做法是:
- 将其中所有 public/protected 方法提炼为一个或多个接口(如
CrudService<t></t>、AsyncNotifyCapable) - 把共用逻辑下沉到工具类或默认方法(Java 8+ 的 interface default 方法仅限简单委托,不放核心业务)
- 模块间只通过这些接口通信,具体实现由各自模块提供并注入
这样,消费者模块只依赖 OrderQueryService 接口,完全不知道也不关心背后是内存缓存实现、数据库实现,还是远程 RPC 实现。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











