依赖倒置靠面向接口编程:高层模块(如orderservice)只依赖paymentprocessor等接口,而非具体实现类;接口应无状态、方法签名稳定、不暴露实现细节,避免抽象类、硬编码和领域层污染。

Java 里怎么用接口实现依赖倒置
依赖倒置不是靠抽象类或工具库,而是靠「面向接口编程」——把具体实现类从调用方的代码里彻底踢出去。核心动作就一个:让高层模块(比如 OrderService)只依赖 PaymentProcessor 这样的接口,而不是 AlipayProcessor 或 WechatPayProcessor 这种具体类。
常见错误现象:OrderService 构造函数里直接 new AlipayProcessor(),或者用 if (type == "alipay") return new AlipayProcessor() —— 这等于把实现细节硬编码进业务逻辑,一加新支付方式就得改 OrderService。
- 必须定义清晰的接口,方法签名要稳定,比如
process(PaymentRequest request),别塞进跟支付宝强相关的字段(如notifyUrl) - 实现类只负责自己那一块逻辑,不暴露内部构造细节;接口里不要定义
setXXX()方法来“注入”配置,那是违反封装的临时补丁 - Spring 的
@Autowired是辅助手段,但依赖倒置本身不依赖 Spring;纯 Java SE 也能做,靠工厂或手动传参即可
为什么不能用抽象类代替接口来倒置依赖
抽象类容易让人误以为“继承就是解耦”,结果写出 AlipayProcessor extends AbstractPayment,而 OrderService 却依赖 AbstractPayment —— 看似抽象,实则还是绑死了继承链。一旦需要组合多个行为(比如同时支持异步回调和重试),抽象类就僵住了。
接口更轻、更正交,一个类可以实现多个接口(PaymentProcessor + AsyncCapable),而抽象类只能单继承。更重要的是:接口天然不带状态和实现,能真正守住“只约定行为,不管怎么做”的边界。
立即学习“Java免费学习笔记(深入)”;
- 抽象类如果有非空构造函数、protected 字段或模板方法,调用方就可能无意中依赖这些实现细节
- 接口方法默认 public,避免访问权限混乱;抽象类方法可以是 protected,容易引发“我是不是该去覆写这个?”的误判
- Java 8+ 接口可有 default 方法,但仅限于通用辅助逻辑(如日志包装),不能放核心流程,否则又变相绑定实现
Spring 中 @Autowired 不等于完成了依赖倒置
@Autowired 只解决“谁来创建实例”的问题,不解决“依赖是否抽象”。如果 @Autowired private AlipayProcessor processor;,那还是紧耦合——编译期就绑死了类型,IDE 会报错,单元测试也绕不开真实支付网关。
正确姿势是:接口类型注入 + 明确的 Bean 定义边界。Spring 只是让这件事更容易落地,不是替代设计判断。
- 确保
@Autowired的字段类型是接口,比如private PaymentProcessor processor; - 在配置类或
@Bean方法里决定具体用哪个实现,且该配置应与业务逻辑隔离(比如放在PaymentConfig.java,而非OrderService.java) - 避免在接口上加
@Component或@Service—— 接口不是组件,实现类才是 - 测试时用
@MockBean PaymentProcessor mockProcessor;才有效;若 mock 的是具体类,说明倒置没到位
容易被忽略的边界:领域层不该持有基础设施接口
比如在 Order 实体类里加个 public void pay(PaymentProcessor processor) 方法,看起来倒置了,实则污染了领域模型——Order 应该只关心“是否已支付”,不关心“怎么付”。支付是应用层或基础设施层的事。
真正的分层倒置,是让应用层(如 PlaceOrderUseCase)持有 PaymentProcessor,再把它传给领域服务,或者通过事件/回调通知领域对象状态变更。
- 领域对象里出现任何基础设施相关接口名(
PaymentProcessor、NotificationSender、CacheClient),基本可以判定倒置越界 - 接口命名要反映业务意图,而不是技术选型,比如用
PaymentGateway比HttpClientBasedPaymentAdapter更合适 - 当多个模块都要用同一类能力(如发短信),别为了复用而让它们都依赖同一个接口;考虑按上下文拆成
VerificationCodeSender和OrderStatusNotifier,职责更清晰










