阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 抽象类与抽象方法

抽象类与抽象方法

作者:陈川 阅读数:48218人阅读 分类: TypeScript

抽象类与抽象方法的基本概念

抽象类是一种特殊的类,它不能被实例化,只能被继承。抽象类中可以包含抽象方法,这些方法只有声明没有实现,必须在子类中被具体实现。抽象类的主要作用是定义一组公共的接口和行为规范,强制子类遵循特定的结构。

abstract class Animal {
  abstract makeSound(): void;
  
  move(): void {
    console.log("Moving...");
  }
}

抽象类的特点

抽象类与普通类有几个关键区别:

  1. 抽象类必须使用abstract关键字声明
  2. 抽象类不能直接实例化
  3. 抽象类可以包含抽象方法和具体实现的方法
  4. 抽象类可以有构造函数
  5. 抽象类可以包含成员变量
abstract class Department {
  constructor(public name: string) {}
  
  abstract printMeeting(): void;
  
  printName(): void {
    console.log("Department name: " + this.name);
  }
}

抽象方法的特性

抽象方法是抽象类中的核心概念,具有以下特点:

  1. 必须使用abstract关键字标记
  2. 不能有具体实现(没有方法体)
  3. 必须在派生类中实现
  4. 可以包含参数和返回类型声明
  5. 访问修饰符可以是public、protected或private
abstract class Shape {
  abstract getArea(): number;
  abstract getPerimeter(): number;
  
  displayInfo(): void {
    console.log(`Area: ${this.getArea()}, Perimeter: ${this.getPerimeter()}`);
  }
}

实现抽象类的子类

当继承抽象类时,子类必须实现所有抽象方法,否则子类也必须声明为抽象类。

class Circle extends Shape {
  constructor(private radius: number) {
    super();
  }
  
  getArea(): number {
    return Math.PI * this.radius * this.radius;
  }
  
  getPerimeter(): number {
    return 2 * Math.PI * this.radius;
  }
}

const circle = new Circle(5);
circle.displayInfo(); // 输出: Area: 78.53981633974483, Perimeter: 31.41592653589793

抽象类与接口的区别

虽然抽象类和接口在某些方面相似,但它们有重要区别:

特性 抽象类 接口
实现 可以包含具体实现 只有声明
成员变量 可以有 不能有
构造函数 可以有 不能有
多重继承 不支持 支持
访问修饰符 可以有 默认public
// 接口示例
interface ClockInterface {
  currentTime: Date;
  setTime(d: Date): void;
}

// 抽象类示例
abstract class ClockAbstract {
  abstract currentTime: Date;
  abstract setTime(d: Date): void;
  getTime(): Date {
    return this.currentTime;
  }
}

抽象类的实际应用场景

抽象类特别适合以下场景:

  1. 定义框架或库的基础结构
  2. 强制派生类实现特定方法
  3. 提供部分通用实现
  4. 创建具有共同行为的类家族
abstract class DataAccess<T> {
  abstract connect(): Promise<void>;
  abstract disconnect(): Promise<void>;
  abstract query(sql: string): Promise<T[]>;
  
  async executeTransaction(queries: string[]): Promise<void> {
    try {
      await this.connect();
      for (const query of queries) {
        await this.query(query);
      }
    } finally {
      await this.disconnect();
    }
  }
}

class MySQLAccess extends DataAccess<any> {
  async connect(): Promise<void> { /* MySQL连接实现 */ }
  async disconnect(): Promise<void> { /* MySQL断开连接实现 */ }
  async query(sql: string): Promise<any[]> { /* MySQL查询实现 */ }
}

抽象类中的构造函数

抽象类可以有构造函数,虽然不能直接实例化抽象类,但子类可以通过super()调用父类的构造函数。

abstract class Person {
  constructor(protected name: string, protected age: number) {}
  
  abstract introduce(): string;
}

class Employee extends Person {
  constructor(name: string, age: number, private jobTitle: string) {
    super(name, age);
  }
  
  introduce(): string {
    return `Hi, I'm ${this.name}, ${this.age} years old, working as ${this.jobTitle}`;
  }
}

const emp = new Employee("Alice", 30, "Developer");
console.log(emp.introduce());

抽象属性

除了抽象方法,TypeScript还支持抽象属性,这些属性必须在派生类中实现。

abstract class Vehicle {
  abstract brand: string;
  abstract model: string;
  abstract year: number;
  
  getFullDescription(): string {
    return `${this.year} ${this.brand} ${this.model}`;
  }
}

class Car extends Vehicle {
  brand = "Toyota";
  model = "Camry";
  year = 2020;
}

const myCar = new Car();
console.log(myCar.getFullDescription()); // 输出: 2020 Toyota Camry

抽象类与多态

抽象类是实现多态的重要方式,通过抽象类定义的接口,可以在运行时决定调用哪个具体实现。

abstract class PaymentProcessor {
  abstract processPayment(amount: number): boolean;
}

class CreditCardProcessor extends PaymentProcessor {
  processPayment(amount: number): boolean {
    console.log(`Processing credit card payment of $${amount}`);
    return true;
  }
}

class PayPalProcessor extends PaymentProcessor {
  processPayment(amount: number): boolean {
    console.log(`Processing PayPal payment of $${amount}`);
    return true;
  }
}

function handlePayment(processor: PaymentProcessor, amount: number) {
  processor.processPayment(amount);
}

const creditCard = new CreditCardProcessor();
const paypal = new PayPalProcessor();

handlePayment(creditCard, 100); // Processing credit card payment of $100
handlePayment(paypal, 50); // Processing PayPal payment of $50

抽象类与设计模式

抽象类在许多设计模式中扮演重要角色,如模板方法模式、工厂方法模式等。

// 模板方法模式示例
abstract class DataExporter {
  // 模板方法
  export(): void {
    this.prepareData();
    this.validateData();
    this.sendData();
    this.cleanup();
  }
  
  protected abstract prepareData(): void;
  protected abstract validateData(): void;
  
  protected sendData(): void {
    console.log("Sending data to server...");
  }
  
  protected cleanup(): void {
    console.log("Cleaning up temporary files...");
  }
}

class CSVExporter extends DataExporter {
  protected prepareData(): void {
    console.log("Preparing CSV data...");
  }
  
  protected validateData(): void {
    console.log("Validating CSV data...");
  }
}

const exporter = new CSVExporter();
exporter.export();

抽象类的局限性

虽然抽象类很强大,但也有其局限性:

  1. TypeScript是单继承语言,一个类只能继承一个抽象类
  2. 抽象类增加了层级关系,可能使代码结构复杂
  3. 过度使用抽象类可能导致设计过度工程化
// 单继承限制示例
abstract class A {
  abstract methodA(): void;
}

abstract class B {
  abstract methodB(): void;
}

// 错误:不能同时继承A和B
class C extends A /*, B*/ {
  methodA(): void { /* 实现 */ }
  // methodB(): void { /* 无法同时实现 */ }
}

抽象类与混入(Mixins)

虽然TypeScript不支持多重继承,但可以通过混入模式模拟从多个抽象类继承行为。

// 混入模式示例
class Disposable {
  isDisposed: boolean = false;
  dispose() { this.isDisposed = true; }
}

class Activatable {
  isActive: boolean = false;
  activate() { this.isActive = true; }
  deactivate() { this.isActive = false; }
}

// 使用混入
class SmartObject implements Disposable, Activatable {
  constructor() {
    setInterval(() => console.log(this.isActive + " : " + this.isDisposed), 500);
  }
  
  // Disposable
  isDisposed: boolean = false;
  dispose: () => void;
  
  // Activatable
  isActive: boolean = false;
  activate: () => void;
  deactivate: () => void;
}

applyMixins(SmartObject, [Disposable, Activatable]);

function applyMixins(derivedCtor: any, baseCtors: any[]) {
  baseCtors.forEach(baseCtor => {
    Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
      derivedCtor.prototype[name] = baseCtor.prototype[name];
    });
  });
}

抽象类与依赖注入

抽象类常用于依赖注入场景,作为服务契约定义接口。

abstract class LoggerService {
  abstract log(message: string): void;
  abstract error(message: string): void;
  abstract warn(message: string): void;
}

class ConsoleLogger extends LoggerService {
  log(message: string): void {
    console.log(`[LOG] ${message}`);
  }
  
  error(message: string): void {
    console.error(`[ERROR] ${message}`);
  }
  
  warn(message: string): void {
    console.warn(`[WARN] ${message}`);
  }
}

class App {
  constructor(private logger: LoggerService) {}
  
  run() {
    this.logger.log("Application started");
    this.logger.warn("This is a warning");
    this.logger.error("This is an error");
  }
}

const app = new App(new ConsoleLogger());
app.run();

抽象类与访问修饰符

抽象类中的成员可以有不同的访问级别,控制派生类和外部代码的访问权限。

abstract class AccessExample {
  public abstract publicMethod(): void;
  protected abstract protectedMethod(): void;
  private privateMethod(): void {
    console.log("This is private");
  }
  
  // 抽象私有方法在TypeScript 4.3+支持
  // private abstract privateAbstractMethod(): void;
}

class AccessImpl extends AccessExample {
  public publicMethod(): void {
    console.log("Public method implementation");
  }
  
  protected protectedMethod(): void {
    console.log("Protected method implementation");
    // this.privateMethod(); // 错误:无法访问私有方法
  }
  
  // 不能实现父类的私有抽象方法
  // private privateAbstractMethod(): void {}
}

const instance = new AccessImpl();
instance.publicMethod();
// instance.protectedMethod(); // 错误:受保护方法只能在类或其子类中访问

抽象类与静态成员

抽象类可以包含静态成员,这些成员属于类本身而不是实例。

abstract class MathOperations {
  static PI: number = 3.14159;
  
  static calculateCircleArea(radius: number): number {
    return this.PI * radius * radius;
  }
  
  abstract calculate(): number;
}

class Square extends MathOperations {
  constructor(private side: number) {
    super();
  }
  
  calculate(): number {
    return this.side * this.side;
  }
}

console.log(MathOperations.calculateCircleArea(5)); // 78.53975
const square = new Square(4);
console.log(square.calculate()); // 16

抽象类与泛型

抽象类可以与泛型结合使用,创建更灵活的类型结构。

abstract class Repository<T> {
  abstract getAll(): T[];
  abstract getById(id: number): T | undefined;
  abstract add(item: T): void;
  abstract update(item: T): boolean;
  abstract delete(id: number): boolean;
}

class UserRepository extends Repository<User> {
  private users: User[] = [];
  
  getAll(): User[] {
    return [...this.users];
  }
  
  getById(id: number): User | undefined {
    return this.users.find(u => u.id === id);
  }
  
  add(user: User): void {
    this.users.push(user);
  }
  
  update(user: User): boolean {
    const index = this.users.findIndex(u => u.id === user.id);
    if (index >= 0) {
      this.users[index] = user;
      return true;
    }
    return false;
  }
  
  delete(id: number): boolean {
    const initialLength = this.users.length;
    this.users = this.users.filter(u => u.id !== id);
    return this.users.length !== initialLength;
  }
}

interface User {
  id: number;
  name: string;
  email: string;
}

抽象类与装饰器

抽象类可以与装饰器一起使用,增强类的功能。

function sealed(constructor: Function) {
  Object.seal(constructor);
  Object.seal(constructor.prototype);
}

@sealed
abstract class SealedAbstract {
  abstract method(): void;
  
  concreteMethod() {
    console.log("Concrete implementation");
  }
}

// 尝试修改SealedAbstract会失败
// SealedAbstract.prototype.newMethod = function() {}; // 错误:无法添加属性

抽象类与条件类型

在高级类型场景中,抽象类可以与条件类型结合使用。

abstract class ShapeBase {
  abstract area(): number;
}

class Circle extends ShapeBase {
  constructor(private radius: number) { super(); }
  area(): number { return Math.PI * this.radius ** 2; }
}

class Square extends ShapeBase {
  constructor(private side: number) { super(); }
  area(): number { return this.side ** 2; }
}

type ShapeType<T extends ShapeBase> = T extends Circle ? "circle" : "square";

function getShapeType<T extends ShapeBase>(shape: T): ShapeType<T> {
  return shape instanceof Circle ? "circle" : "square" as ShapeType<T>;
}

const circle = new Circle(5);
const square = new Square(4);

console.log(getShapeType(circle)); // "circle"
console.log(getShapeType(square)); // "square"

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌