Abstraction (Abstraktion)
Kurz: Das Prinzip, komplexe interne Details hinter einer einfachen, klar definierten Schnittstelle zu verstecken — man muss wissen, WAS etwas tut, nicht WIE es das im Detail macht.
Genauer: Ein Auto-Fahrer muss nicht wissen, wie der Motor intern funktioniert, um zu fahren — Gaspedal und Lenkrad reichen als Schnittstelle. Genauso definiert eine Schnittstelle oder abstrakte Klasse, WELCHE Methoden es gibt, ohne deren konkrete Umsetzung festzulegen — das überlässt sie den implementierenden Klassen. Abstraktion ist eines der vier Grundprinzipien der OOP.
Im Detail
Abstraktion begegnet einem auf mehreren Ebenen gleichzeitig, und es lohnt sich, sie auseinanderzuhalten:
- Datenabstraktion: Ein Datentyp wie
Stackbietetpush()/pop()an, ohne zu verraten, ob intern ein Array oder eine verkettete Liste verwendet wird. - Kontrollabstraktion: Eine Funktion
sortiere(liste)versteckt, welcher konkrete Sortieralgorithmus dahintersteckt. - Schnittstellenabstraktion: Ein
Interfaceoder eine abstrakte Klasse legt nur fest, WELCHE Methoden existieren müssen — nicht, wie sie implementiert sind. - Abstraktionsebenen im Großen: Ein ganzes System lässt sich in Schichten denken (Datenbank-Ebene, Geschäftslogik-Ebene, Präsentations-Ebene) — jede Schicht abstrahiert die darunterliegende und bietet der darüberliegenden eine vereinfachte Sicht.
Ein einfaches Python-Beispiel zur Kontrollabstraktion:
def berechne_gesamtpreis(warenkorb):
# Aufrufer muss nicht wissen, wie Rabatte/Steuern intern berechnet werden
zwischensumme = summiere(warenkorb)
return wende_steuer_und_rabatt_an(zwischensumme)Derselbe Gedanke lässt sich mit einem Interface in TypeScript auf der Typebene ausdrücken — der Aufrufer kennt nur die Form, nicht die Implementierung:
interface Zahlungsanbieter {
zahleAb(betrag: number): Promise<boolean>;
}
// Zwei völlig unterschiedliche Implementierungen, austauschbar hinter derselben Abstraktion
class StripeZahlung implements Zahlungsanbieter { /* ... */ }
class PaypalZahlung implements Zahlungsanbieter { /* ... */ }
function checkout(anbieter: Zahlungsanbieter, betrag: number) {
// Diese Funktion ist komplett egal, WELCHER Anbieter dahintersteckt
return anbieter.zahleAb(betrag);
}Der große Vorteil: Wenn sich die interne Umsetzung ändert (z. B. ein effizienterer Algorithmus, eine andere Datenstruktur, ein neuer Zahlungsanbieter), muss niemand, der die Abstraktion nutzt, seinen eigenen Code anpassen — solange die Schnittstelle gleich bleibt. Das senkt die Kopplung zwischen Programmteilen und macht große Codebasen wartbar, weil man beim Arbeiten an einem Modul nicht das gesamte System im Kopf behalten muss. In der Softwarearchitektur wird das oft als “Dependency Inversion” bezeichnet: höhere Ebenen hängen von einer Abstraktion ab, nicht von einer konkreten Implementierung.
Wann Abstraktion “leckt”
Eine Abstraktion ist nie hundertprozentig perfekt — der Begriff “Leaky Abstraction” (undichte Abstraktion) beschreibt Fälle, in denen Details der Implementierung trotzdem durchsickern und den Nutzer der Abstraktion betreffen. Ein Datenbank-ORM abstrahiert SQL zwar weitgehend weg, aber ein ineffizientes N+1-Abfragemuster (siehe OOP-nahe Datenzugriffsmuster) merkt man trotzdem als spürbare Performance-Probleme — die Abstraktion “SQL ist nicht mehr sichtbar” hält der Realität nicht ganz stand. Das ist keine Rechtfertigung, auf Abstraktion zu verzichten, aber ein Hinweis, dass man das darunterliegende System nie komplett ignorieren sollte, nur weil eine schöne Schnittstelle davor steht.
Zu viel des Guten
Der typische Fallstrick ist zu viel Abstraktion (sogenanntes “Over-Engineering” oder “Speculative Generality”): eine Schnittstelle, ein Plugin-System oder eine Konfigurierbarkeit für einen Anwendungsfall zu bauen, den es (noch) gar nicht gibt, macht Code komplizierter statt einfacher — jede zusätzliche Abstraktionsebene ist auch eine zusätzliche Stelle, an der man beim Lesen des Codes nachdenken und springen muss. Faustregel (auch bekannt als “Rule of Three”): erst konkret implementieren, dann abstrahieren, sobald sich mindestens ein zweiter, echter Anwendungsfall zeigt — nicht schon beim ersten, rein hypothetischen.
Abgrenzung zur Kapselung: Kapselung schützt Daten vor unkontrolliertem Zugriff (das WIE bleibt verborgen, weil es verboten ist, direkt heranzukommen), Abstraktion reduziert Komplexität, indem sie Details gar nicht erst zeigt (das WIE ist irrelevant für die Nutzung). Beide arbeiten meist Hand in Hand, sind aber unabhängige Konzepte — man kann etwas abstrahieren, ohne es zu kapseln (eine öffentliche Schnittstelle ohne Zugriffsschutz auf die Interna) und umgekehrt.
Siehe auch: Interface, OOP, Encapsulation