Appearance
接口、抽象类与实体类对比
这三个概念是面向对象设计中不同层次的抽象工具,它们不是替代关系,而是分层协作关系。
概念定位
- 接口(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 | 直接实例化 |
常见反模式
- 把接口当”字段清单”:接口应该描述行为(方法),不仅仅是数据结构。如果一个接口只有 getter/setter,考虑是否应该用 type 代替。
- 抽象类承担过多业务逻辑:如果抽象类中的具体方法超过 50%,它已经不像”抽象”了,考虑将公共逻辑抽到独立的工具类或 service 中。
- 深继承链:超过 3 层的继承往往意味着抽象层次过多,维护和理解成本上升。优先使用组合(Composition)替代继承。
TypeScript 特有的考量
TypeScript 的 interface 和 type 在大多数场景下可以互换,但有关键差异:
interface支持声明合并(declaration merging),type不支持type支持联合类型和交叉类型,interface不支持- 对类的实现(
implements)只能用interface或class,不能用type
选择原则:对外暴露的公共 API 优先用 interface(利用声明合并的扩展性);内部类型运算用 type(利用联合/交叉类型的灵活性)。
