TypeScript Abstrakte Klassen

Abstrakte Klassen sind ein wichtiges Konzept in der objektorientierten Programmierung mit TypeScript.

Es ist eine Klasse, die nicht direkt instanziiert werden kann und nur als Basisklasse für die Vererbung durch andere Klassen dienen kann.

Abstrakte Klassen werden hauptsächlich verwendet, um gemeinsame Eigenschaften und Methoden für Unterklassen zu definieren und bieten eine einheitliche Struktur- und Verhaltensvorlage für Unterklassen.


SVG-Diagramm: Vererbungshierarchie abstrakter Klassen Background Title Abstract Class Inheritance Hierarchy Structure Abstract class Animal abstract class Animal Cannot be instantiated directly Arrows to subclasses Subclass Dog class Dog extends Animal Subclass Cat class Cat extends Animal Abstract method description Abstract class features Feature list abstract method: subclasses must implement it Concrete method: subclasses can use it directly Can be used as a type: accepts subclass instances Arrow marker

Warum abstrakte Klassen benötigt werden

In der objektorientierten Programmierung müssen wir oft Basisklassen definieren.

Diese Basisklassen beschreiben gemeinsame Eigenschaften und Methoden für Unterklassen, aber die Implementierungsdetails einiger Methoden müssen von den Unterklassen selbst entschieden werden.

Abstrakte Klassen werden verwendet, um dieses Problem zu lösen: Sie ermöglichen es uns, Methodensignaturen (Deklarationen) zu definieren und die konkrete Implementierung den Unterklassen zu überlassen.

Konzepterklärung:Eine abstrakte Klasse ist ein Typ zwischen einer normalen Klasse und einer Schnittstelle. Sie besitzt sowohl Schnittstellenmerkmale (Definition von Methodensignaturen) als auch Klassenmerkmale (kann konkrete Methodenimplementierungen und Konstruktoren haben).


Grundlagen abstrakter Klassen

Verwenden Sieabstractdas Schlüsselwort `abstract`, um abstrakte Klassen und abstrakte Methoden zu deklarieren.

Abstrakte Klassen können abstrakte Methoden und konkrete Methoden (Methoden mit vorhandener Implementierung) enthalten.

Beispiel

// Verwende das Schlüsselwort abstract, um eine abstrakte Klasse zu deklarieren
// Eine abstrakte Klasse kann nicht direkt instanziiert werden; sie kann nur als Basisklasse dienen
abstract class Animal {
    // Eigenschaft für den Namen des Tieres
    name: string;

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

    // Abstrakte Methode: mit abstract modifiziert, kein Methodenrumpf
    // Unterklassen müssen diese Methode implementieren
    abstract speak(): void;

    // Konkrete Methode: eine Methode mit einer spezifischen Implementierung
    // Unterklassen können sie direkt erben und verwenden, ohne sie zu überschreiben
    move(): void {
        console.log(this.name + " bewegt sich");
    }
}

// Der Versuch, eine abstrakte Klasse zu instanziieren, führt zu einem Fehler
// var animal = new Animal("Animal"); // Fehler: eine abstrakte Klasse kann nicht instanziiert werden

// Definiere die Klasse Dog, die von Animal erbt
class Dog extends Animal {
    // Die Unterklasse muss die abstrakte Methode speak() der Elternklasse implementieren
    speak(): void {
        console.log(this.name + " Wuff wuff!");
    }
}

// Erstelle eine Dog-Instanz
var dog = new Dog("Wangcai");
dog.speak();  // Rufe die von der Unterklasse implementierte Methode auf
dog.move();   // Konkrete Methode von der Elternklasse erben

Ausgabe:

Wangcai, woof woof woof!
Wangcai is moving.

Erklärung:Abstrakte Methoden haben keinen Methodenkörper (nur eine Methodensignatur), und Unterklassen müssen sie implementieren. Konkrete Methoden haben spezifische Implementierungen, und Unterklassen können sie direkt erben und verwenden.


Abstrakte Methoden

Abstrakte Methoden sind Methoden, die in einer abstrakten Klasse deklariert, aber nicht implementiert werden.

Nachdem eine Unterklasse von einer abstrakten Klasse erbt, muss sie alle abstrakten Methoden implementieren; andernfalls tritt ein Fehler auf.

Beispiel

// Abstrakte Klasse Shape definieren
abstract class Shape {
    // Abstrakte Methode: nur Deklaration, keine Implementierung
    // Unterklassen müssen diese beiden Methoden implementieren
    abstract area(): number;
    abstract perimeter(): number;

    // Konkrete Methode: kann abstrakte Methoden verwenden
    // Diese Methode ruft eine abstrakte Methode auf; Unterklassen können sie nach der Vererbung normal verwenden
    describe(): void {
        console.log("Fläche: " + this.area().toFixed(2));
    }
}

// Definiere eine Rechteckklasse, die von Shape erbt
class Rectangle extends Shape {
    // Breite des Rechtecks
    width: number;
    // Höhe des Rechtecks
    height: number;

    // Konstruktor
    constructor(width: number, height: number) {
        super();  // Konstruktor der Elternklasse aufrufen
        this.width = width;
        this.height = height;
    }

    // Abstrakte Methode implementieren: Fläche berechnen
    area(): number {
        return this.width * this.height;
    }

    // Abstrakte Methode implementieren: Umfang berechnen
    perimeter(): number {
        return 2 * (this.width + this.height);
    }
}

// Eine Rechteckinstanz erstellen
var rect = new Rectangle(4, 5);

// Die geerbte describe-Methode aufrufen, die intern die area-Methode der Unterklasse aufruft
rect.describe();
console.log("Umfang: " + rect.perimeter());

Ausgabe:

面积: 20.00
周长: 18

Tipp:Konkrete Methoden können abstrakte Methoden aufrufen. Das liegt daran, dass die Unterklasse, wenn eine konkrete Methode aufgerufen wird, die abstrakte Methode bereits implementiert hat, sodass sie normal ausgeführt werden kann.


Abstrakte Klasse als Typ

Eine abstrakte Klasse kann als Parametertyp verwendet werden.

Das bedeutet, dass eine Funktion Instanzen jeder Unterklasse der abstrakten Klasse akzeptieren kann, wodurch Polymorphie erreicht wird.

Beispiel

// Abstrakte Klasse Animal definieren
abstract class Animal {
    // Abstrakte Methode: Alle Tiere geben Laute von sich
    abstract speak(): void;
}

// Cat-Klasse definieren, die von Animal erbt
class Cat extends Animal {
    // Abstrakte Methode implementieren
    speak(): void {
        console.log("Miau miau miau!");
    }
}

// Dog-Klasse definieren, die von Animal erbt
class Dog extends Animal {
    // Abstrakte Methode implementieren
    speak(): void {
        console.log("Wuff wuff wuff!");
    }
}

// Den Funktion-Parameter-Typ als abstrakte Klasse Animal definieren
// Diese Funktion kann jede Instanz einer Unterklasse von Animal akzeptieren
function makeSpeak(animal: Animal): void {
    animal.speak();
}

// Das Übergeben verschiedener Unterklasseninstanzen führt zu unterschiedlichem Verhalten (Polymorphismus)
makeSpeak(new Cat());
makeSpeak(new Dog());

// Array vom Typ abstrakte Klasse: kann Instanzen verschiedener Unterklassen speichern
var animals: Animal[] = [new Cat(), new Dog()];

Ausgabe:

Meow meow meow!
Woof woof woof!

Polymorphismus:Wenn eine abstrakte Klasse als Typ verwendet wird, ist die tatsächlich ausgeführte Methodenimplementierung die der Unterklasse. Dies ist das Polymorphismus-Feature der objektorientierten Programmierung.


Unterschiede zwischen abstrakten Klassen und Schnittstellen

Sowohl abstrakte Klassen als auch Schnittstellen werden verwendet, um Typspezifikationen zu definieren, aber es gibt einige wichtige Unterschiede.

Das Verständnis ihrer Unterschiede hilft, in der tatsächlichen Entwicklung die richtige Wahl zu treffen.

Merkmal Abstrakte Klasse Benutzeroberfläche
实例化 Kann nicht direkt instanziiert werden Kann nicht direkt instanziiert werden
Methodenimplementierung Kann konkrete Implementierungen haben Kann nur Deklarationen haben (TypeScript 3.6+ kann Standardimplementierungen haben)
Member-Modifizierer Kann public, protected, private hinzufügen Kann nur readonly haben
Vererbung Einfachvererbung (kann nur eine Klasse erweitern) Mehrfachimplementierung (kann mehrere Interfaces implementieren)
Konstruktor Kann einen Konstruktor haben Kann keinen Konstruktor haben

Auswahlvorschläge:Verwenden Sie abstrakte Klassen, wenn gemeinsamer Code benötigt wird; verwenden Sie Schnittstellen, wenn Spezifikationen/Verträge definiert werden müssen; verwenden Sie Schnittstellen, wenn Mehrfachvererbung benötigt wird.


Vollständiges Beispiel: Zahlungssystem

Nachfolgend ein Beispiel für ein Zahlungssystem, das mit einer abstrakten Klasse implementiert wurde.

Dieses Beispiel demonstriert die Anwendung abstrakter Klassen in realen Projekten.

Beispiel

// Abstrakte Zahlungsklasse definieren
abstract class Payment {
    // Abstrakte Methode: Zahlung verarbeiten, Unterklassen müssen sie implementieren
    abstract process(amount: number): boolean;

    // Konkrete Methode: Zahlungsinformationen überprüfen
    // Alle Unterklassen können diese Methode verwenden
    validate(): void {
        console.log("Zahlungsinformationen überprüfen");
    }
}

// Klasse für Kreditkartenzahlung
class CreditCardPayment extends Payment {
    // Kreditkartennummer
    cardNumber: string;

    // Konstruktor
    constructor(cardNumber: string) {
        super();  // Konstruktor der Elternklasse aufrufen
        this.cardNumber = cardNumber;
    }

    // Abstrakte Methode implementieren: Kreditkartenzahlung verarbeiten
    process(amount: number): boolean {
        console.log("Kreditkartenzahlung wird verarbeitet: " + amount);
        return true;
    }
}

// Klasse für PayPal-Zahlung
class PayPalPayment extends Payment {
    // PayPal-E-Mail
    email: string;

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

    // Abstrakte Methode implementieren: PayPal-Zahlung verarbeiten
    process(amount: number): boolean {
        console.log("PayPal-Zahlung wird verarbeitet: " + amount);
        return true;
    }
}

// Polymorphismus verwenden, um verschiedene Zahlungsmethoden zu behandeln
var payments: Payment[] = [
    new CreditCardPayment("1234"),
    new PayPalPayment("[email protected]")
];

// Über jede Zahlungsmethode iterieren und verarbeiten
for (var _i = 0, payments_1 = payments; _i < payments_1.length; _i++) {
    var payment = payments_1[_i];
    // Die geerbte Verifizierungsmethode aufrufen
    payment.validate();
    // Die Verarbeitungsmethode der Unterklasse aufrufen
    payment.process(100);
}

Ausgabe:

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

Anwendungsszenarien:Abstrakte Klassen werden häufig beim Framework-Design und bei der Schichtung der Geschäftslogik verwendet. Zum Beispiel Zahlungsabwicklung, Benutzerauthentifizierung, Datenpersistenz und weitere Szenarien.


Hinweise

  • Können nicht instanziiert werden:Abstrakte Klassen können nicht direkt mit new instanziiert werden; sie müssen von Unterklassen geerbt werden.
  • Abstrakte Methoden müssen implementiert werden:Ein Fehler tritt auf, wenn eine Unterklasse nicht alle abstrakten Methoden implementiert.
  • Können Konstruktoren haben:Abstrakte Klassen können Konstruktoren definieren, und Unterklassen müssen super() aufrufen.
  • Einfachvererbung:Eine Klasse kann nur von einer abstrakten Klasse erben (Einfachvererbung).
  • Zugriffsmodifikatoren:Abstrakte Methoden können die Modifikatoren public und protected verwenden.

Best Practices:Verwenden Sie abstrakte Klassen, wenn mehrere Klassen Code und Logik gemeinsam nutzen müssen. Verwenden Sie Schnittstellen, wenn nur Spezifikationen und Verträge definiert werden müssen.


Zusammenfassung

Abstrakte Klassen sind ein wichtiger Bestandteil der objektorientierten Programmierung in TypeScript.

  • Das Schlüsselwort abstract:Wird verwendet, um abstrakte Klassen und Methoden zu deklarieren
  • Kann nicht instanziiert werden:Kann nur durch Vererbung von Unterklassen verwendet werden
  • Abstrakte Methoden:Methoden, die von Unterklassen implementiert werden müssen
  • Konkrete Methoden:Implementierte Methoden, die Unterklassen direkt erben und verwenden können
  • Schablonenmethoden-Muster:Abstrakte Klassen definieren das Gerüst, Unterklassen stellen konkrete Implementierungen bereit

Empfehlung:Beim Entwurf von Klassenhierarchien sollten vorrangig abstrakte Klassen verwendet werden, um Code gemeinsam zu nutzen und Spezifikationen zu definieren.

Weitere Erweiterungen