接口设计体现“弱耦合”,核心是调用方只依赖接口契约而不感知实现类;接口无状态、无实现、仅声明行为;依赖须在运行时注入,禁用new硬编码;接口应小粒度、职责单一、命名业务化;测试可替换mock即解耦到位;异常应业务化而非技术化。

接口在设计上体现“弱耦合”,核心是让调用方完全不知道也不需要知道实现类的存在——它只认接口定义的行为,不关心谁来执行、怎么执行。
接口只声明契约,不携带实现细节
接口本身没有字段、没有构造器、不保存状态,也不提供默认逻辑(除非必要且谨慎)。它只回答一个问题:“你能做什么?”比如:
-
Logger 接口只声明
log(String msg),不规定日志存哪、是否异步、要不要格式化 -
PaymentProcessor 只定义
process(PaymentRequest request),不暴露支付宝还是微信的签名流程
这种纯行为抽象,天然隔绝了实现差异。只要实现类遵守这个契约,替换它就像换电池一样,对使用者零感知。
依赖必须发生在运行时,而非编译期或代码内
真正的弱耦合,体现在代码里找不到 new XxxImpl() 这类硬编码。常见安全做法包括:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 构造器注入:业务类通过参数接收接口类型,由外部容器或测试代码传入具体实现
- 工厂返回:
PaymentProcessor processor = PaymentFactory.create("alipay"),但工厂内部封装了 new 逻辑,调用方仍只面对接口 - ServiceLoader 或 Spring @Autowired:依赖由框架在启动时解析并装配,类本身无创建责任
一旦你在某个 Service 类里写了 new DatabaseLogger(),哪怕它实现了 Logger 接口,耦合就已经发生——编译期就绑定了具体类。
接口粒度小、语义专一,避免“大而全”
一个接口如果同时包含 sendEmail()、generateReport()、validateUser(),那它本质是多个职责的拼凑体。实现类要么被迫实现无关功能,要么用空方法应付,反而加剧耦合。
- 推荐按场景拆分:EmailSender、ReportGenerator、UserValidator
- 每个接口方法名聚焦业务动作:
reserveSeat()比updateBookingTable()更清晰,也更易替换 - 参数和返回值用明确结构:
Result<booking></booking>或Optional<user></user>,不传Map<string object></string>这种模糊载体
测试可替换性是解耦的试金石
如果一个类依赖 NotificationService,你能否在单元测试中直接 new 一个轻量 Mock 实现传进去,而不启动数据库、不走网络、不发真实短信?能,说明解耦到位;不能,说明还有隐式依赖没剥离。
- Mock 实现只需覆盖接口方法,无需任何框架(如 Mockito 是锦上添花,不是必需)
- 若测试时必须加载 Spring 上下文才能跑通,往往意味着配置耦合或自动装配逻辑过于复杂
- 接口里尽量不抛技术异常(如 SQLException),而是用业务结果枚举或自定义业务异常,降低调用方处理负担
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










