抽象类和接口是解决不同设计问题的工具:抽象类适合共享状态与默认行为、构建“is-a”层级;接口适合定义能力契约、支持多维组合;实际中常协同使用,如抽象类封装主流程,接口注入策略。

抽象类和接口不是非此即彼的选择题,而是解决不同设计问题的两种工具。关键不在“哪个更好”,而在“哪个更贴合当前业务语义与演化需求”。
当需要共享状态和默认行为时,优先考虑抽象类
抽象类可以包含字段、构造器、已实现的方法,甚至有访问修饰符控制继承细节。如果多个子类天然共用某些数据(如订单编号、创建时间)或基础逻辑(如日志记录、参数校验),抽象类能避免重复代码,也便于统一维护。
- 适合构建有明确“is-a”关系的层级结构,比如 PaymentProcessor 抽象类下派生 WeChatPayProcessor 和 AlipayProcessor,它们共享支付流水号生成规则和风控检查入口
- 若未来可能增加受保护的中间层逻辑(如统一加解密、幂等性处理),抽象类比接口更容易平滑演进
- 注意:Java 中一个类只能继承一个抽象类,所以它更适合定义核心主干能力,而非横向能力扩展
当强调能力契约与多维组合时,接口是更自然的选择
接口只声明“能做什么”,不关心“怎么做”或“有什么”。它支持一个类同时实现多个接口,适合描述角色、能力或跨领域协作协议。
- 例如电商系统中,Refundable、Trackable、Cancelable 这些接口可被订单、物流单、售后单等不同实体自由组合,无需强耦合到同一继承链
- 从 JDK 8 开始,接口可带 default 和 static 方法,允许提供轻量级通用实现(如 JSON 序列化模板、ID 生成辅助),但依然不能持有实例状态
- 微服务间定义 API 协议(如 OpenAPI)或内部模块间解耦通信(如事件监听器),接口更能体现“契约先行”的设计思想
现实项目中,常采用“抽象类 + 接口”协同模式
大型业务系统往往既需要统一骨架,又需灵活扩展能力。典型做法是:用抽象类封装领域主干流程,用接口注入可插拔策略。
- 比如 AbstractOrderService 定义下单主流程(校验→锁库存→生成单据→发消息),而将“库存锁定方式”抽为 InventoryLockStrategy 接口,由 RedisLock 或 DB Lock 实现类动态注入
- Spring 框架大量使用该模式:JdbcDaoSupport 是抽象类,提供 JdbcTemplate 封装;而 PlatformTransactionManager 是接口,适配 JDBC、JTA、Reactive 等多种事务机制
- 这种组合兼顾了复用性与开放性,也符合“面向接口编程,依赖倒置”的原则
判断决策树:三步快速定位选型
遇到新模块设计时,可依次回答:
- 是否需要保存共享状态? → 是 → 抽象类更合适
- 是否要求一个类型具备多种独立能力? → 是 → 接口更合适
- 是否已有成熟基类且需保留其构造逻辑? → 是 → 接口无法替代抽象类,但可让该基类再实现若干接口以增强语义
不复杂但容易忽略:命名本身就在传递设计意图——叫 AbstractXXX 暗示它是骨架,叫 XXXable 或 XXXService 则暗示它是契约。让名字说话,比争论语法特性更有价值。










