多态本身不自动降低耦合,关键在于接口由调用方定义并持有、实现方仅被动实现;接口命名体现业务意图、契约屏蔽实现细节、参数返回值使用业务dto、依赖通过构造器或setter注入。

多态本身不自动降低耦合,关键在于接口引用由谁定义、谁持有、谁实例化——只有调用方(稳定方)定义并持有接口,实现方仅被动实现且不反向依赖,才能真正切断模块间强绑定。
接口必须由调用方定义和发布
不是支付模块写一个 IPayment 接口让订单模块去用,而是订单模块定义 IChargeHandler,支付模块只实现它。这样订单不依赖支付的任何代码,编译期也不会引入支付模块的 jar 或包路径。
- IDE 中 Ctrl+Click 接口名,应跳转到订单工程的
com.example.order.contract包下 - 支付模块的 pom.xml 里不能有对订单模块的
<scope>compile</scope>依赖 - 接口命名体现业务意图(如
processCharge()),而非技术实现(如callAlipayApi())
接口契约要屏蔽实现细节
接口方法签名不能泄露三方 SDK、框架注解或运行时机制,否则调用方就被实现细节“污染”,切换实现时不得不改接口、改所有调用点。
- 异常类型统一为业务语义异常,如
PaymentFailedException,而非AlipayApiException - 返回值用简单 POJO,如
ChargeResult(含success、traceId、message),不带支付宝或微信的字段 - 禁止在接口方法上加
@RequestBody、@Valid、@JsonIgnore等框架级注解
运行时对象创建必须可控、可替换
调用方通过构造器或 setter 注入接口引用,而不是用 ServiceLoader.load() 或 ApplicationContext.getBean() 这类隐式查找方式——后者会把容器、反射、生命周期管理等非业务逻辑悄悄引入依赖链。
- 单元测试时可直接 new 实现类传入,无需启动 Spring 容器
- 不同环境(测试/灰度/生产)可注入不同实现,比如模拟支付、沙箱支付、真实支付
- 避免静态工厂、单例全局查找,它们会让依赖关系隐藏在运行时,破坏编译期隔离
参数与返回值需封装为业务 DTO
接口方法的入参和出参不应是实体类、VO 或三方模型,而应是专为此契约设计的轻量 DTO,按业务变化原因聚合字段,不随数据库表或 API 响应结构频繁变动。
- 例如传入
ChargeRequest(含orderId、amount、currency),而非OrderEntity - DTO 字段命名用业务语言(
payeeAccount),不用技术术语(alipayUserId) - DTO 不继承、不包含框架注解,也不实现任何业务逻辑
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











