接口定义“能做什么”,抽象类封装“共用怎么做”;接口用于跨模块契约通信,抽象类用于模块内公共逻辑复用,二者协同配合依赖注入实现多模块解耦。

Java 中接口与抽象类实现多模块解耦,核心在于明确分工:接口定义“能做什么”,抽象类封装“共用怎么做”,二者配合可分层隔离变化点,让模块之间只通过契约通信,不感知彼此内部细节。
接口负责跨模块行为契约
接口是纯粹的抽象声明,不带状态、不依赖具体实现,天然适合模块边界定义。比如订单服务模块和通知服务模块之间,用 NotificationService 接口约定 send(String content) 方法,订单模块只调用该接口,完全不关心是短信、邮件还是站内信实现。
- 多个模块可各自实现同一接口(如 OrderProcessorImpl、MockOrderProcessor),运行时动态替换,无需改调用方代码
- 接口应小而专(单一职责),避免大而全的 NotificationServiceV2 —— 这样便于组合,也降低修改引发的连锁影响
- 慎用 default 方法:一旦添加,所有实现类自动继承逻辑,后续调整可能破坏已有行为;若必须扩展,优先考虑新增接口或版本化命名
抽象类聚焦模块内公共实现复用
当多个同类实现共享初始化逻辑、模板流程或基础字段时,抽象类比接口更合适。例如日志模块中,FileLogger 和 DatabaseLogger 都需要校验日志级别、格式化时间戳、处理异常重试——这些可抽到 AbstractLogger 中,子类只专注写入动作本身。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 抽象类可含 protected 字段、构造器、非抽象方法,适合承载模块内状态和通用流程
- 它不用于跨模块通信,而是作为实现类的“内部基座”,避免重复编码
- 一个类只能继承一个抽象类,所以它不适合表达“多种能力”,比如不能同时继承 AbstractLogger 和 AbstractNotifier —— 这正是接口要补位的地方
组合使用构建分层解耦结构
真实系统中,接口与抽象类常协同工作。以支付模块为例:
立即学习“Java免费学习笔记(深入)”;
- 定义 PaymentProcessor 接口,作为对外契约(各业务模块依赖它)
- 提供 AbstractAlipayProcessor 抽象类,封装支付宝 SDK 初始化、签名验签、异步回调解析等共用逻辑
- 具体实现类如 AlipayWebProcessor、AlipayAppProcessor 继承该抽象类,并实现差异化的请求组装与响应处理
- 外部模块(如订单服务)只持有 PaymentProcessor 引用,通过依赖注入获得具体实例,全程不触碰抽象类或实现类名
依赖注入是解耦落地的关键支撑
仅有接口和抽象类还不够,必须让运行时对象按需装配。硬编码 new 实现类会立刻破坏解耦效果。
- 构造器注入最清晰:类在创建时由外部传入接口类型参数,测试时可直接塞入 Mock 实现
- 工厂模式适用于条件分支多的场景:PaymentFactory.create(type) 返回对应 Processor,调用方不暴露 if-else
- Spring 等容器通过 @Autowired 按接口类型查找 Bean,要求接口有唯一实现(或用 @Qualifier 显式指定),避免 NoUniqueBeanDefinitionException

















