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
¿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
// 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
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
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
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.
Otras extensionesRecomendación:Al diseñar jerarquías de clases, prioriza el uso de clases abstractas para compartir código y definir especificaciones.