接口定义能力契约,抽象类封装通用逻辑,具体类专注实现,三者协作配合策略与工厂模式实现开闭原则;优先组合而非深层继承,如用装饰器扩展行为。

接口和抽象类不是非此即彼的选择,而是互补的协作关系。真正优雅的设计,是让接口定义契约、抽象类沉淀共性、具体类专注实现——三者各司其职,再配合合适的设计模式,代码才既灵活又可维护。
用接口定义能力契约,不暴露实现细节
接口应聚焦“能做什么”,而非“怎么做”。比如日志模块中,不叫 FileLogger 或 Log4jLogger,而是定义 Logger 接口:
public interface Logger {
void info(String msg);
void error(String msg, Throwable t);
boolean isEnabled();
}
这样上层代码只依赖 Logger,后续可自由切换 SLF4J、Logback、云日志服务,甚至 mock 测试实现,完全无感知。
用抽象类封装通用逻辑,避免重复代码
当多个实现共享初始化、模板流程或工具方法时,抽象类就派上用场。例如所有日志器都要处理时间戳、线程 ID、日志级别过滤:
立即学习“Java免费学习笔记(深入)”;
public abstract class AbstractLogger implements Logger {
protected final String prefix;
protected AbstractLogger(String prefix) {
this.prefix = prefix;
}
@Override
public void info(String msg) {
log("INFO", msg);
}
@Override
public void error(String msg, Throwable t) {
log("ERROR", msg);
if (t != null) logStackTrace(t);
}
// 模板方法:子类决定如何真正输出
protected abstract void doWrite(String level, String content);
private void log(String level, String msg) {
String fullMsg = String.format("[%s][%s][%s] %s",
LocalDateTime.now(), Thread.currentThread().getName(), level, msg);
doWrite(level, fullMsg);
}
private void logStackTrace(Throwable t) {
// 格式化堆栈等通用逻辑
}
}
子类只需关心最核心的写入动作(如写文件、发 HTTP、存 DB),其余都由抽象类兜底。
结合策略模式 + 工厂模式,运行时动态组合
把接口作为策略入口,抽象类作为策略基座,再用工厂统一创建,就能轻松支持配置驱动的行为切换:
- 定义
PaymentStrategy接口(支付能力) - 提供
AbstractPayment抽象类(含签名验签、幂等处理、异步回调模板) - 实现
WechatPayment、AlipayPayment等具体类 - 通过
PaymentFactory根据订单类型或配置返回对应策略实例
新增一种支付方式?加一个实现类 + 注册到工厂,其他代码零修改。这就是开闭原则的自然体现。
谨慎使用抽象类继承链,优先组合优于继承
多层抽象(如 BaseLogger → AsyncLogger → FileAsyncLogger)容易导致脆弱性。更推荐:
- 抽象类只做一层通用封装(如上面的
AbstractLogger) - 用组合代替深层继承:比如把“异步能力”抽成
AsyncWrapper装饰器,包装任意Logger - 装饰器本身也实现
Logger接口,保持类型一致,无缝接入现有逻辑
这样扩展新行为(重试、限流、采样)不再需要改继承结构,只需新增装饰器,符合单一职责和组合复用原则。


















