跳到主要内容

接口 vs 抽象类

基本概念与区别

抽象类的特性

  1. 定义特点

    • 不允许被实例化,只能被继承
    • 可以包含属性和方法
    • 方法可以有具体实现,也可以是抽象方法
    • 子类必须实现所有抽象方法
  2. 代码示例

abstract class Logger {
private name: string;
private enabled: boolean;
private minPermittedLevel: Level;

constructor(name: string, enabled: boolean, minPermittedLevel: Level) {
this.name = name;
this.enabled = enabled;
this.minPermittedLevel = minPermittedLevel;
}

public log(level: Level, message: string): void {
const loggable = this.enabled && (this.minPermittedLevel <= level);
if (!loggable) return;
this.doLog(level, message);
}

protected abstract doLog(level: Level, message: string): void;
}

接口的特性

  1. 定义特点

    • 不能包含属性(除了静态常量)
    • 只能声明方法,方法不能包含实现代码
    • 类必须实现接口中声明的所有方法
  2. 代码示例

interface Filter {
doFilter(req: RpcRequest): void;
}

class AuthenticationFilter implements Filter {
doFilter(req: RpcRequest): void {
// 鉴权逻辑实现
}
}

本质区别

  1. 关系类型

    • 抽象类:表示 is-a 关系,是一种继承关系
    • 接口:表示 has-a 关系,是一种能力的声明
  2. 设计目的

    • 抽象类:主要用于代码复用
    • 接口:主要用于解耦,是一种行为的抽象
  3. 设计思路

    • 抽象类:自下而上的设计思路,从具体到抽象
    • 接口:自上而下的设计思路,先定义接口再考虑实现

如何选择使用

  1. 使用抽象类的场景

    • 需要表示 is-a 关系
    • 需要在多个子类之间共享代码
    • 需要继承默认实现的方法
    • 需要声明非 public 的成员
  2. 使用接口的场景

    • 需要表示 has-a 关系
    • 需要实现多个接口(多继承)
    • 需要定义一组行为规范
    • 需要提高代码的可扩展性

最佳实践建议

  1. 选择标准

    • 如果主要目的是代码复用,使用抽象类
    • 如果主要目的是定义规范,使用接口
    • 如果不确定,优先使用接口
  2. 设计建议

    • 接口设计要保持简单,单一职责
    • 抽象类层次不要过深
    • 优先使用组合而非继承
    • 面向接口编程而非实现

总结

接口和抽象类是面向对象编程中两个重要的概念,各自有其适用场景:

  • 抽象类通过继承关系实现代码复用,适合表示具有层次结构的对象关系
  • 接口通过定义行为契约实现解耦,适合定义对象的能力

在实际开发中,应该根据具体场景选择合适的方式,可以同时使用两者来实现更灵活的设计。记住一点:抽象类是对"类"的抽象,而接口是对"行为"的抽象。