面向接口编程的核心是划清责任边界、约束协作方式、隔离变化源头,使业务模块仅依赖“做什么”而非“怎么做”,实现更换实现时业务代码零修改。

面向接口编程在业务架构中落地,关键不是堆砌接口数量,而是用接口划清责任边界、约束协作方式、隔离变化源头。它最终要让业务模块只依赖“做什么”,不关心“怎么做”,更换实现时业务代码零修改。
聚焦业务契约,定义窄而专的接口
接口不是功能清单,而是职责明确的业务契约。一个接口只管一件事,方法不超过3个,参数尽量控制在2个以内。
- 比如订单服务需要发通知,就定义NotificationService接口,只含send(Notice notice)一个方法;重试、日志、渠道选择这些都拆成独立接口(RetryPolicy、DeliveryLog、ChannelSelector)
- 避免把send()、retry()、logFailure()塞进同一个接口——这会让多个职责强耦合,新增短信通道时就得改发券、重试、日志三块逻辑
- 接口里不放常量(如MAX_RETRY = 3),那是实现细节,应由具体类自己管理
构造器注入 + 接口持有,杜绝硬编码依赖
业务类不能自己 new 实现类,也不能用 if-else 判断类型再创建对象。所有依赖必须通过外部传入,且字段类型必须是接口。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 正确做法:在OrderService构造方法中接收PaymentProcessor processor,由 Spring 或测试代码传入具体实现
- 错误写法:new AlipayClient()出现在业务方法里;或if (channel.equals("wechat")) { new WechatPayClient() }
- 没有 Spring 时,可用工厂类统一创建(如PaymentFactory.get(channel)),但调用方仍只认PaymentProcessor接口
策略化多变逻辑,运行时可插拔替换
当同一动作有多种实现路径(如不同用户等级享不同运费规则、不同活动发不同优惠券),不要用分支硬编码,而要抽象为策略接口。
- 定义FreightCalculator接口,含calculate(Order order)方法
- 实现类:StandardFreightCalculator、VipFreightCalculator、PromotionFreightCalculator
- OrderService只持FreightCalculator接口,通过配置或上下文决定注入哪个实现;新增一种计费规则,只需加实现类+配置,原代码不动
分层隔离 + 包结构协同演进
接口要和模块边界对齐,不同层只依赖本层所需接口,不跨层持有具体技术组件。
- Controller 层只依赖 Service 接口(如UserService),不碰UserMapper或RestTemplate
- Service 层只依赖 Repository/Client/Validator 接口,绝不持有JdbcTemplate或RedisTemplate
- 包结构建议按业务域组织(如com.example.order.service、com.example.order.repository),接口与其实现类可同包或分包,但实现包名需体现技术细节(如...repository.jdbc、...repository.cache)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










