TypeScript Clases abstractas

La clase abstracta (Abstract Class) es un concepto importante en la programación orientada a objetos de TypeScript.

Es una clase que no puede instanciarse directamente; solo puede servir como clase base para que otras clases la hereden.

La clase abstracta se utiliza principalmente para definir las propiedades y métodos comunes de las subclases, proporcionando a las subclases una plantilla unificada de estructura y comportamiento.


Diagrama SVG: jerarquía de herencia de clases abstractas Fondo Título Estructura jerárquica de herencia de clases abstractas Clase abstracta Animal abstract class Animal No se puede instanciar directamente Flecha hacia las subclases Subclase Dog class Dog extends Animal Subclase Cat class Cat extends Animal Explicación del método abstracto Características de la clase abstracta Lista de características Método abstract: la subclase debe implementarlo Método concreto: la subclase puede usarlo directamente Se puede usar como tipo: acepta instancias de subclases Marcas de flecha

¿Por qué se necesitan las clases abstractas?

En la programación orientada a objetos, a menudo necesitamos definir clases base.

Estas clases base describen las propiedades y métodos comunes de las subclases, pero algunos detalles de implementación de métodos deben ser decididos por la propia subclase.

La clase abstracta sirve para resolver este problema: nos permite definir las firmas (declaraciones) de los métodos, dejando la implementación concreta a las subclases.

Explicación del concepto:La clase abstracta es un tipo intermedio entre una clase normal y una interfaz. Tiene características de interfaz (definir firmas de métodos) y también características de clase (puede tener implementaciones de métodos concretos y un constructor).


Fundamentos de las clases abstractas

Usoabstractde la palabra clave para declarar clases abstractas y métodos abstractos.

La clase abstracta puede contener métodos abstractos y métodos concretos (métodos con implementación).

Ejemplo

// Declarar la clase abstracta con la palabra clave abstract
// La clase abstracta no se puede instanciar directamente, solo como clase base
abstract class Animal {
    // Propiedad del nombre del animal
    name: string;

    // Constructor
    constructor(name: string) {
        this.name = name;
    }

    // Método abstracto: modificado con abstract, sin cuerpo de método
    // La subclase debe implementar este método
    abstract speak(): void;

    // Método concreto: método con implementación específica
    // La subclase puede heredarlo y usarlo directamente, no necesita sobrescribirlo
    move(): void {
        console.log(this.name + " se está moviendo");
    }
}

// Intentar instanciar la clase abstracta provocará un error
// var animal = new Animal("animal"); // Error: no se puede instanciar una clase abstracta

// Definir la clase Dog que hereda de Animal
class Dog extends Animal {
    // La subclase debe implementar el método abstracto speak() de la clase padre
    speak(): void {
        console.log(this.name + " ¡Guau guau guau!");
    }
}

// Crear una instancia de Dog
var dog = new Dog("Fortuna");
dog.speak();  // Llamar al método implementado por la subclase
dog.move();   // Heredar el método concreto de la clase padre

Resultado de la ejecución:

Wangcai ¡guau guau guau! Wangcai se está moviendo.

Explicación:El método abstracto no tiene cuerpo (solo firma del método); la subclase debe implementarlo. El método concreto tiene una implementación específica; la subclase puede heredarlo y usarlo directamente.


Métodos abstractos

Un método abstracto es un método que en la clase abstracta solo se declara pero no se implementa.

Después de que una subclase hereda una clase abstracta, debe implementar todos los métodos abstractos; de lo contrario, se producirá un error.

Ejemplo

// Definir la clase abstracta Shape (figura)
abstract class Shape {
    // Método abstracto: solo declaración, sin implementación
    // La subclase debe implementar estos dos métodos
    abstract area(): number;
    abstract perimeter(): number;

    // Método concreto: puede usar métodos abstractos
    // Este método llama al método abstracto; después de heredarlo, la subclase puede usarlo con normalidad
    describe(): void {
        console.log("Área: " + this.area().toFixed(2));
    }
}

// Definir la clase Rectángulo que hereda de Shape
class Rectangle extends Shape {
    // Ancho del rectángulo
    width: number;
    // Alto del rectángulo
    height: number;

    // Constructor
    constructor(width: number, height: number) {
        super();  // Llamar al constructor de la clase padre
        this.width = width;
        this.height = height;
    }

    // Implementar el método abstracto: calcular el área
    area(): number {
        return this.width * this.height;
    }

    // Implementar el método abstracto: calcular el perímetro
    perimeter(): number {
        return 2 * (this.width + this.height);
    }
}

// Crear una instancia de rectángulo
var rect = new Rectangle(4, 5);

// Llamar al método describe heredado; internamente llamará al método area de la subclase
rect.describe();
console.log("Perímetro: " + rect.perimeter());

Resultado de la ejecución:

面积: 20.00
周长: 18

Sugerencia:Un método concreto puede llamar a un método abstracto. Esto se debe a que, cuando se llama al método concreto, la subclase ya ha implementado el método abstracto, por lo que puede ejecutarse correctamente.


La clase abstracta como tipo

La clase abstracta se puede usar como tipo de parámetro.

Esto significa que una función puede aceptar instancias de cualquier subclase de la clase abstracta, logrando polimorfismo.

Ejemplo

// Definir la clase abstracta Animal
abstract class Animal {
    // Método abstracto: todos los animales emiten un sonido
    abstract speak(): void;
}

// Definir la clase Cat que hereda de Animal
class Cat extends Animal {
    // Implementar el método abstracto
    speak(): void {
        console.log("¡Miau miau miau!");
    }
}

// Definir la clase Dog que hereda de Animal
class Dog extends Animal {
    // Implementar el método abstracto
    speak(): void {
        console.log("¡Guau guau guau!");
    }
}

// Definir el tipo del parámetro de la función como la clase abstracta Animal
// Esta función puede aceptar cualquier instancia de subclase de Animal
function makeSpeak(animal: Animal): void {
    animal.speak();
}

// Al pasar diferentes instancias de subclase, se logran diferentes comportamientos (polimorfismo)
makeSpeak(new Cat());
makeSpeak(new Dog());

// Array de tipo clase abstracta: puede almacenar instancias de diferentes subclases
var animals: Animal[] = [new Cat(), new Dog()];

Resultado de la ejecución:

¡Miau miau miau! ¡Guau guau guau!

Polimorfismo:Cuando la clase abstracta se usa como tipo, lo que se ejecuta realmente es la implementación del método de la subclase. Esta es la característica de polimorfismo de la programación orientada a objetos.


Diferencias entre clases abstractas e interfaces

Las clases abstractas y las interfaces se utilizan para definir especificaciones de tipos, pero existen algunas diferencias clave.

Comprender sus diferencias ayuda a tomar la decisión correcta en el desarrollo real.

Característica Clase abstracta Interfaz
Instanciación No se puede instanciar directamente No se puede instanciar directamente
Implementación de métodos Puede tener implementación concreta Solo puede tener declaraciones (TypeScript 3.6+ puede tener implementación predeterminada)
Modificadores de miembros Puede agregar public, protected, private Solo puede tener readonly
Herencia Herencia única (solo puede extender una clase con extends) Implementación múltiple (puede implementar varias interfaces con implements)
Constructor Puede tener constructor No puede tener constructor

Sugerencia para elegir:Si necesita compartir código, use clase abstracta; si necesita definir una especificación/contrato, use interfaz; si necesita herencia múltiple, use interfaz.


Ejemplo completo: sistema de pagos

A continuación se muestra un ejemplo de un sistema de pagos implementado con clases abstractas.

Este ejemplo muestra la aplicación de las clases abstractas en proyectos reales.

Ejemplo

// Definir la clase de pago abstracta
abstract class Payment {
    // Método abstracto: procesar el pago, la subclase debe implementarlo
    abstract process(amount: number): boolean;

    // Método concreto: validar la información de pago
    // Todas las subclases pueden usar este método
    validate(): void {
        console.log("Validando la información de pago");
    }
}

// Clase de pago con tarjeta de crédito
class CreditCardPayment extends Payment {
    // Número de tarjeta de crédito
    cardNumber: string;

    // Constructor
    constructor(cardNumber: string) {
        super();  // Llamar al constructor de la clase padre
        this.cardNumber = cardNumber;
    }

    // Implementar el método abstracto: procesar pago con tarjeta de crédito
    process(amount: number): boolean {
        console.log("Procesando pago con tarjeta de crédito: " + amount);
        return true;
    }
}

// Clase de pago PayPal
class PayPalPayment extends Payment {
    // Correo electrónico de PayPal
    email: string;

    // Constructor
    constructor(email: string) {
        super();
        this.email = email;
    }

    // Implementar el método abstracto: procesar pago con PayPal
    process(amount: number): boolean {
        console.log("Procesando pago con PayPal: " + amount);
        return true;
    }
}

// Usar polimorfismo para procesar diferentes métodos de pago
var payments: Payment[] = [
    new CreditCardPayment("1234"),
    new PayPalPayment("[email protected]")
];

// Recorrer y procesar cada método de pago
for (var _i = 0, payments_1 = payments; _i < payments_1.length; _i++) {
    var payment = payments_1[_i];
    // Llamar al método de validación heredado
    payment.validate();
    // Llamar al método de procesamiento de la subclase
    payment.process(100);
}

Resultado de la ejecución:

验证支付信息
处理信用卡支付: 100
验证支付信息
处理 PayPal 支付: 100

Escenarios de aplicación:Las clases abstractas se utilizan a menudo en el diseño de frameworks y en la estratificación de la lógica de negocio, por ejemplo, en escenarios como procesamiento de pagos, autenticación de usuarios y persistencia de datos.


Notas

  • No se puede instanciar:Las clases abstractas no pueden crear instancias directamente con `new`, deben heredarse mediante subclases.
  • Deben implementarse los métodos abstractos:Si una subclase no implementa todos los métodos abstractos, se producirá un error.
  • Pueden tener constructores:Las clases abstractas pueden definir constructores, y las subclases deben llamar a `super()`.
  • Herencia única:Una clase solo puede heredar de una clase abstracta (herencia única).
  • Modificadores de acceso:Los métodos abstractos pueden usar los modificadores `public` y `protected`.

Buenas prácticas:Cuando varias clases necesitan compartir código y lógica, usa clases abstractas. Cuando solo se necesita definir especificaciones y contratos, usa interfaces.


Resumen

Las clases abstractas son un componente importante de la programación orientada a objetos en TypeScript.

  • La palabra clave `abstract`:Se utiliza para declarar clases abstractas y métodos.
  • No se puede instanciar:Solo se puede usar mediante herencia de subclases.
  • Métodos abstractos:Métodos que las subclases deben implementar.
  • Métodos concretos:Métodos con implementación que las subclases pueden heredar y usar directamente.
  • Patrón de método plantilla:La clase abstracta define el esqueleto, y las subclases proporcionan la implementación concreta.

Recomendación:Al diseñar jerarquías de clases, prioriza el uso de clases abstractas para compartir código y definir especificaciones.

Otras extensiones