Warum Vererbung im UI toxisch werden kann
Einordnung
Vererbung ist ein zentrales Konzept der objektorientierten Programmierung.
Sie dient der Strukturierung von Verhalten und der Wiederverwendung von Code.
Im UI-Kontext – insbesondere in Angular – führt sie jedoch häufig zu strukturellen Architekturproblemen.
Dieses Dokument analysiert warum.
1. Was Vererbung eigentlich bedeutet
Vererbung modelliert eine „is-a“-Beziehung.
class Animal {}
class Dog extends Animal {}
Ein Hund ist ein Tier.
Vererbung eignet sich daher für:
- stabile Taxonomien
- echte Spezialisierung
- Polymorphie im Domänenmodell
2. Was UI-Komponenten sind (und nicht sind)
Eine Angular-Komponente ist:
- eine Projektion von State
- ein Rendering-Knoten
- ein Adapter zwischen Datenfluss und View
Sie ist keine Domänenentität.
Sie ist kein Objekt mit Identität im fachlichen Sinn.
Sie ist eine Ableitung.
3. Das typische Muster
class DestroyableComponent {
protected destroy$ = new Subject<void>();
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}
class UserComponent extends DestroyableComponent {}
Begründung:
„Wir wollen Unsubscribe zentral lösen.“
Das klingt harmlos.
Ist es aber nicht.
4. Das strukturelle Problem
4.1 Keine echte „is-a“-Beziehung
Eine UserComponent ist keine DestroyableComponent.
Sie hat Lifecycle.
Sie ist nicht Lifecycle.
Hier wird Vererbung für ein Querschnittsproblem missbraucht.
4.2 Vererbung koppelt stärker als beabsichtigt
Mit extends wird gekoppelt:
- Lifecycle-Implementierung
- interne Felder
- Hook-Reihenfolge
- implizite Seiteneffekte
- zukünftige Erweiterungen der Basisklasse
Diese Kopplung ist:
- global
- implizit
- schwer sichtbar
4.3 Basisklassen wachsen
Vererbung erzeugt natürliche Akkumulation:
Erst:
- destroy$
Dann:
- loading$
- error handling
- logging
- permission checks
- routing helpers
- feature flags
Die Basisklasse wird zur Infrastruktur-Gottklasse.
Alle Komponenten hängen daran.
5. Warum das im UI besonders gefährlich ist
Frontend-Systeme sind:
- lifecycle-sensitiv
- zustandsabhängig
- reaktiv
- nebenläufig
Vererbte Lifecycle-Logik erzeugt:
- versteckte Kontrollflüsse
- implizite Hooks
- schwer nachvollziehbare Ausführungsketten
Debugging wird schwieriger, weil Verhalten nicht lokal sichtbar ist.
6. Vererbung und reaktive Architektur widersprechen sich
Reaktive Systeme basieren auf:
- Komposition von Datenflüssen
- Transformation
- Ableitung
Vererbung hingegen basiert auf:
- Hierarchie
- Identität
- impliziter Strukturweitergabe
Das sind unterschiedliche Denkmodelle.
Reaktivität ist flach und kompositorisch.
Vererbung ist hierarchisch und strukturell.
7. Versteckte Seiteneffekte
Ein häufiger Effekt:
ngOnDestroy() {
super.ngOnDestroy();
}
Oder schlimmer:
Super-Implementierungen werden vergessen.
Oder Hooks werden überschrieben.
Das erzeugt fragile Lifecycle-Ketten.
8. Testbarkeit leidet
Eine Komponente mit Vererbung:
- ist nicht isoliert testbar
- hat implizites Verhalten
- benötigt Knowledge über Basisklasse
Mocks werden komplexer.
Tests werden indirekter.
9. Verletzung des Single Responsibility Principle
Komponente sollte:
UI-Projektion kapseln.
Durch Vererbung übernimmt sie zusätzlich:
- Infrastruktur
- Lifecycle-Management
- Fehlerbehandlung
- Nebenlogik
Verantwortlichkeiten vermischen sich.
10. Die eigentliche Ursache
Das Vererbungsproblem entsteht oft aus einem anderen Anti-Pattern:
.subscribe()
Manuelles Subscription-Management erzeugt Bedarf nach:
- destroy$
- Unsubscribe-Handling
- Lifecycle-Hooks
Würde man:
- async pipe
- takeUntilDestroyed()
- Signals
- deklarative Streams
verwenden, wäre keine Basisklasse nötig.
Vererbung ist hier Symptom, nicht Ursache.
11. Architekturprinzip: Composition over Inheritance
Statt:
extends BaseComponent
besser:
- Utility-Funktionen
- RxJS-Operatoren
- Angular Built-ins
- Dependency Injection
- Signal-basierte Ableitung
Komposition ist:
- lokal
- explizit
- testbar
- sichtbar
Vererbung ist:
- global
- implizit
- wachsend
- schwer entfernbar
12. Wann Vererbung im UI vertretbar ist
Nur wenn:
- echte Spezialisierung vorliegt
- Template bewusst erweitert wird
- polymorphes Verhalten gewünscht ist
- Hierarchie stabil ist
Nicht für:
- Lifecycle-Handling
- Unsubscribe-Logik
- Logging
- Error-Handling
- Infrastruktur
13. Kernaussage
Vererbung im UI wird toxisch, wenn sie verwendet wird für:
- Querschnittsfunktionalität
- Lifecycle-Management
- imperatives Ressourcenhandling
Sie erzeugt:
- Gott-Basisklassen
- implizite Kopplung
- wachsende Komplexität
- architektonische Fragilität
UI-Systeme profitieren von:
- flacher Struktur
- funktionaler Komposition
- deklarativer Ableitung
- klarer Datenflussarchitektur
Architekturregel
UI-Komponenten sollen Verhalten komponieren, nicht Hierarchien erben.