Solid Reihe - Liskov Substitution Principle

Solid Reihe - Liskov Substitution Principle
12/2024
Kai P. | Solution Engineer

TL;DR

Das Liskov Substitution Principle (LSP) besagt, dass Subtypen jederzeit ihre Basistypen ersetzen können müssen, ohne das erwartete Verhalten zu verändern. Klassische Verstöße, etwa das Quadrat/Rechteck-Problem, zeigen, wie falsche Vererbung zu fehlerhaftem Design führt. Auch in verteilten Systemen und Schnittstellenarchitekturen spielt das LSP eine zentrale Rolle für saubere, erweiterbare Softwarestrukturen.

LSP, das Liskov Substitution Principle

Diese Solid Reihe zum Liskov Substitution Principle orientiert sich an der Clean Architecture von Robert C. Martin. (ISBN-13: 978-0-13-449416-6).

Definition des LSP

Barbara Liskov beschrieb eine Vorgehensweise zur Definition von Subtypen:

"Für jedes Objekt o1 des Typs S gibt es ein Objekt o2 des Typs T, sodass alle Programme P, die für den Typ T definiert sind, ihr Verhalten nicht verändern, wenn o1 durch o2 ersetzt wird. In diesem Fall ist S ein Subtyp von T."

Zur Verdeutlichung dieser abstrakten Idee sind konkrete Beispiele hilfreich.

Beispiel 1: Subtypen und Vererbung

Angenommen, Sie möchten Lizenzgebühren über die Klasse Abrechnung berechnen, die auf einer Lizenz-Schnittstelle basiert. Diese Schnittstelle definiert die Funktion BerechneGebühr(). Es gibt zwei Subtypen: PersonenLizenz und UnternehmenLizenz. Beide Lizenzarten berechnen die Gebühr mit unterschiedlichen Algorithmen.

Klassendiagramm mit Zahlung, Lizenz-Interface und zwei Implementierungen: Personen- und Unternehmenslizenz.

Der Entwurf erfüllt das LSP, da das Verhalten der Abrechnung unabhängig davon bleibt, welcher Subtyp verwendet wird. Die Subtypen können Lizenz nahtlos ersetzen.

Beispiel 2: Das Quadrat/Rechteck-Problem

Ein klassisches Beispiel für einen Verstoß gegen das LSP ist das Quadrat/Rechteck-Problem.

Klassendiagramm: Benutzer nutzt Rechteck mit Methoden, Quadrat erbt Rechteck und hat eigene Methode.

Ein Rechteck hat unabhängig voneinander veränderbare Breite und Höhe. Im Gegensatz dazu müssen beim Quadrat beide Dimensionen gemeinsam geändert werden. Dadurch ist ein Quadrat kein geeigneter Subtyp eines Rechtecks.

Das folgende Code-Beispiel verdeutlicht diese Problematik:

Um solche Fehler zu vermeiden, sollte der Entwurf alternative Mechanismen zur Erkennung spezifischer Typen bereitstellen.

Architektur und das LSP

Das LSP wird häufig im Softwaredesign angewandt, insbesondere bei der Definition von Schnittstellen und Implementierungen. Diese können sich über verschiedene Ebenen hinweg erstrecken, wie Frontend, Services, Backend und externe Systeme.

Gut durchdachte Schnittstellen sind entscheidend, um einheitliche Implementierungen ohne Missverständnisse und Fehler zu ermöglichen.

Standardisierte Kommunikation

In diesem Abschnitt wird erläutert, was das Liskov Substitution Principle (LSP) nicht ist. Stellen Sie sich vor, wir entwickeln eine Taxi-Bestell-Web-App, die einen Aggregator-Dienst implementiert. Dieser Dienst erstellt ein Angebot verschiedener Taxizentralen, aus dem Kunden eine Auswahl treffen können. Sobald ein Kunde sich für ein Angebot einer Taxizentrale entschieden hat, wird vom System der Fahrer zur Abholung des Kunden ermittelt.

Ein Beispiel: Der Fahrer Kai von DevwareTaxi GmbH besitzt die folgende URI:

  • devwaretaxi.wu/fahrer/kai

Unser System ergänzt diese URI mit Abholdaten für einen PUT-Request, der so aussehen könnte:

  • devwaretaxi.wuppertal/fahrer/kai/abholAdresse/Gueterstr.20/anfrageZeitstempel/20240705162450/anmerkung/JGA

Alle Taxiunternehmen, die Benachrichtigungen an das System senden möchten, müssen standardisiert mit der Schnittstelle kommunizieren. Das betrifft sowohl die Feldbezeichnungen (fahrer, abholAdresse, anfrageZeitstempel, anmerkung) als auch die Datenformatierung, die zwischen Absender und Empfänger übereinstimmen müssen.

Taxi Aggregator zentralisiert die Kommunikation zwischen mehreren Taxiunternehmen mit einheitlicher Schnittstelle.

Beispiel für fehlende Standardisierung

Warum standardisierte Kommunikation erforderlich ist, wird im folgenden Beispiel deutlich:
Ein Entwickler eines Taxiunternehmens führt eine Abkürzung, wie etwa abholAdr anstelle von abholAdresse, ein. Um diese abweichende Anfrage im übergeordneten System zu verarbeiten, müsste im Endpunkt ein Sonderfall implementiert werden. Ein solcher Sonderfall könnte folgendermaßen aussehen:

Taxi Aggregator erhält standardisierte und eine Sonderfall-Kommunikation von abweichendem Taxiunternehmen.

Derartige Sonderfälle widersprechen einer dynamischen Softwarearchitektur. Allein die Abfrage, ob die URI mit einer spezifischen Domäne wie AbweichendesTaxiUnternehmen.de übereinstimmt, ist bereits zu starr. Dadurch entstehen potenzielle Fehlerquellen und Sicherheitslücken.

Erweiterung durch Übernahmen

Zusätzlich wird die Situation komplexer, wenn ein Taxiunternehmen ein anderes übernimmt und beide ihre Webseiten getrennt betreiben, während die Systeme zusammengeführt werden sollen. Müssten wir in einem solchen Szenario nicht einen weiteren Sonderfall implementieren?

Lösung: Abhol-Kommando-Erstellungs-Modul

Ein erfahrener Softwarearchitekt erkennt solche Probleme und isoliert sie. Eine Lösung wäre der Aufbau eines Abhol-Kommando-Erstellungs-Moduls, das mithilfe einer URI-Schlüssel-basierten Datenbanktabelle ein Interpretationsschema für jedes potenzielle Taxiunternehmen bereitstellt. Die Tabelle könnte so aussehen:

URI-basierte Tabelle mit spezifischen Formatierungen für abweichende Taxiunternehmen und Standardfall.

Dadurch würde ein komplexer Mechanismus eingebaut, um Sonderfälle zu behandeln, welche sonst nicht für die Schnittstelle geeignet wären.

Zusammenfassung

Das LSP kann und sollte zur Erweiterung einer Architektur herangezogen werden. Allerdings kann die Missachtung einer gewissen Ersetzbarkeit mit zusätzlichen Mechanismen verbunden sein. In gewachsenen Codebasen lässt sich dieses Prinzip oft erst nachträglich sauber umsetzen. Wir helfen Unternehmen dabei, ihre Legacy-Software zu modernisieren, schrittweise und im laufenden Betrieb.

Kai P. | Solution Engineer
Kai P. | Solution Engineer

Mehr zum Thema

Pfeil nach rechts (Verlinkung)
Mirror-Shape-Pattern für Blazor EditForm
09/2026

Mirror-Shape-Pattern für Blazor EditForm

Blauer Pfeil nach rechts (Verlinkung)
Blazor mit TypeScript: Integration & Best Practices
08/2026

Blazor mit TypeScript: Integration & Best Practices

Blauer Pfeil nach rechts (Verlinkung)
Aspire: Schnellere Entwicklung, einfacherer Start
07/2026

Aspire: Schnellere Entwicklung, einfacherer Start

Blauer Pfeil nach rechts (Verlinkung)
Mirror-Shape-Pattern für Blazor EditForm
09/2026

Mirror-Shape-Pattern für Blazor EditForm

Blauer Pfeil nach rechts (Verlinkung)