接口定义能力契约(如Flyable),抽象类表达本质身份(如AbstractPaymentMethod);应先有具体实现再提炼共性,避免过早抽象;用组合替代伪继承,抽象类仅持组件引用;default方法限无状态工具逻辑,抽象类默认实现需满足复用性、健壮性与可扩展性。

明确分工:接口管“能做什么”,抽象类管“是什么”
接口应聚焦能力契约,比如 Flyable、Encryptable、Retryable,定义一组行为签名,不带状态、不依赖上下文。抽象类则用于表达本质身份,如 AbstractPaymentMethod、AbstractDocumentParser,承载共性结构(字段、初始化逻辑、生命周期方法)和部分稳定实现。
常见错误是把“能飞”硬塞进 AbstractBird 的方法体里,结果鸵鸟、企鹅不得不重写并抛 UnsupportedOperationException。正确做法是让 AbstractBird 只保留鸟类固有属性(喙、卵生、恒温),飞行能力由 Flyable 接口声明,由麻雀、鹰等子类选择实现。
控制抽象粒度:先有具体实现,再提炼共性
不要一上来就建 PaymentProcessor 接口和 AbstractPaymentProcessor 抽象类。先写一个能跑通的 WeChatPayService,等出现第二个明显差异的实现(比如 AlipayService),再反向提取接口;等第三个、第四个出现且复用逻辑稳定时,才考虑引入抽象类封装通用流程(如统一幂等校验、异步回调包装)。
以下信号提示你可能过早抽象:
立即学习“Java免费学习笔记(深入)”;
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 接口只有一个实现类,且短期内无扩展计划
- 抽象类中超过 60% 的方法被子类重写
- 为了“看起来整齐”而为每种渠道建一个子类(WeChatPay、AlipayPay、UnionPay…),但它们之间没有真正需要继承的骨架逻辑
用组合替代“伪继承”,让抽象类保持轻量
抽象类不该成为业务逻辑的容器。避免在 AbstractOrderService 中直接写 saveToDb()、sendSms()、updateInventory() 等跨层操作。这些应拆成独立组件:
- OrderRepository 负责数据存取
- NotificationService 负责通知分发
- InventoryClient 负责库存调用
抽象类只持这些组件的引用,通过调用而非继承获得能力。子类可按需注入不同实现(如测试时用 MockInventoryClient),解耦更彻底,也避免抽象类膨胀成“上帝类”。
谨慎使用 default 方法与抽象类默认实现
接口的 default 方法适合无状态工具逻辑(如 CollectionUtils.isEmpty()),但不适合含业务上下文或资源管理的代码。涉及字段访问、构造器初始化、缓存复用、异常兜底等场景,必须用抽象类实现。
但在抽象类中提供默认实现前,请确认:
- 该逻辑已在至少两个子类中重复出现,且行为高度一致
- 它不依赖未声明的子类特有字段或方法
- 它具备 null 安全、异常传播清晰、线程安全等健壮性保障
- 预留了 beforeXxx()、afterXxx() 等钩子方法,允许子类在关键节点介入
否则,默认实现反而会变成后续演进的枷锁。

















