Skip to content

接口、抽象类与实体类对比

这三个概念是面向对象设计中不同层次的抽象工具,它们不是替代关系,而是分层协作关系。

概念定位

  • 接口(Interface):定义”能做什么”的契约,不包含任何实现。一个类可以实现多个接口。
  • 抽象类(Abstract Class):提供”部分实现 + 强制扩展”。抽象类不能被直接实例化,子类必须实现其抽象方法。
  • 实体类(Concrete Class):完整实现,可以直接实例化使用。

代码示例(TypeScript)

ts
// 接口:定义规范
interface IShape {
  area(): number;
}

// 抽象类:部分实现 + 规范
abstract class Shape implements IShape {
  abstract area(): number;

  describe(): void {
    console.log('This is a shape.');
  }
}

// 实体类:具体实现
class Circle extends Shape {
  constructor(public radius: number) {
    super();
  }

  area(): number {
    return Math.PI * this.radius ** 2;
  }
}

const c = new Circle(5);
console.log(c.area());      // 78.53981633974483
c.describe();               // This is a shape.

选择决策表

场景适合选择原因
定义跨模块的契约Interface零实现,最灵活的抽象
共享部分逻辑 + 强制子类实现Abstract Class代码复用 + 契约约束
完整可用的功能单元Concrete Class直接实例化

常见反模式

  1. 把接口当”字段清单”:接口应该描述行为(方法),不仅仅是数据结构。如果一个接口只有 getter/setter,考虑是否应该用 type 代替。
  2. 抽象类承担过多业务逻辑:如果抽象类中的具体方法超过 50%,它已经不像”抽象”了,考虑将公共逻辑抽到独立的工具类或 service 中。
  3. 深继承链:超过 3 层的继承往往意味着抽象层次过多,维护和理解成本上升。优先使用组合(Composition)替代继承。

TypeScript 特有的考量

TypeScript 的 interfacetype 在大多数场景下可以互换,但有关键差异:

  • interface 支持声明合并(declaration merging),type 不支持
  • type 支持联合类型和交叉类型,interface 不支持
  • 对类的实现(implements)只能用 interfaceclass,不能用 type

选择原则:对外暴露的公共 API 优先用 interface(利用声明合并的扩展性);内部类型运算用 type(利用联合/交叉类型的灵活性)。