Kolumne: Quality Time

Von Weichen über Klassen: Das Liskovsche Substitutionsprinzip
Kommentare

Die Buchstaben S und O der SOLID-Prinzipien [1] nach Robert C. Martin haben wir in unserer kleinen Serie in dieser Kolumne schon behandelt. Falls Sie die ersten beiden Teile der Serie zu diesen essenziellen Regeln der Objektorientierung verpasst haben, können Sie diese unter [2] und [3] nachlesen. Logischerweise folgt nun das L, welches für das so genannte „Liskovsche Substitutionsprinzip“ steht.

PHP Magazin

Die Kolumne „Von Weichen über Klassen“ von Tobias Schlitt ist erstmalig erschienen im PHP Magazin 6.2012

Was seltsam klingt und einen sehr wissenschaftlich-mathematischen Hintergrund hat, erweist sich als eine der wichtigsten Regeln des objektorientierten Softwaredesigns. Sie besagt: Erwartet ein Stück Code ein Objekt einer bestimmten Klasse, so muss es auch mit beliebigen Instanzen von Ableitungen dieser Klasse umgehen können, ohne etwas davon zu wissen. Das klingt zunächst etwas abstrakt und lässt sich am besten an einem Beispiel erklären. Die folgende Klasse wird in einem Projekt zur Umrechnung von Entfernungsangaben verwendet:

class DistanceConverter
{
  const FACTOR = 0.6214;
  public function milesToKilometers( $miles )
  {
    return $miles / self::FACTOR;
  }
}

Die einzige Methode der Klasse erwartet als Eingabe eine Fließkommazahl – eine Entfernung in Meilen – und gibt die konvertierte Kilometerangabe als Fließkommazahl zurück. Ein findiger Entwickler möchte nun zwei Comfort-Probleme dieser Klasse lösen: Zum einen ist die Umrechnung von negativen Distanzen für ihn wenig sinnvoll. Zweitens wird der Rückgabewert im Projekt sowieso nur zur Ausgabe an den Benutzer verwendet, sie kann daher auch direkt hübsch formatiert werden. Um diese beiden Features zu realisieren, baut der Entwickler eine Ableitung der Klasse:

class FormattingDistanceConverter extends DistanceConverer
{
  public function milesToKilometers( $miles )
  {
    if ( $miles 

Was auf den ersten Blick nach einer sinnvollen Ableitung aussieht, kann ganz schnell in einer weißen Seite für den Benutzer oder, noch schlimmer, in einem Stack Trace enden. Denn Code, der auf einen DistanceConverter abgestimmt ist, rechnet nicht mit der nun eingeführten Exception. Und, was passiert, wenn die Rückgabe der Methode milesToKilometers() früher oder später doch irgendwo im Projekt zu weiteren Berechnungen herangezogen wird? Die Definition der Basisklasse lässt diese Möglichkeit ja durchaus offen.

Was ist hier schief gelaufen? Genau, das Liskovsche Substitutionsprinzip wurde verletzt: Code, der einen DistanceConverter verlangt, kann mit der abgeleiteten Klasse nicht mehr so umgehen, wie es die Basisklasse definiert. Damit werden entweder unsägliche Weichen auf Basis der konkreten Klasse notwendig, oder der Fehler im OO-Design muss durch entsprechendes Refactoring behoben werden. Es sollte klar sein, dass nur der zweite Weg eine echte Lösung ist, da das Problem ansonsten alleine durch solche Weichen über kurz oder lang unwartbar wird. Solche Verletzungen des L-Prinzips lassen sich recht einfach durch zwei Regeln erkennen und vermeiden:

  1. Eine überschriebene Methode darf einen größeren Wertebereich akzeptieren als ihr Elter, darf ihn aber auf keinen Fall einschränken
  2. Darf der Wertebereich der Ausgabe höchstens eingeschränkt, aber nicht vergrößert werden

Im gezeigten Beispiel heißt das konkret: Eine Ableitung von DistanceConverter darf ruhig mit anderen Typen als Fließkommazahl umgehen können (z. B. Strings oder Arrays), sie darf aber nicht wie hier gezeigt nur eine Teilmenge der Fließkommazahlen, nämlich positive, akzeptieren. Außerdem dürfte die abgeleitete Methode ruhig nur eine Untermenge der Fließkommazahlen zurückgeben. Die hier gezeigte Erweiterung des Rückgabebereichs auf Strings ist aber ein Tabu.

Die Erfahrung zeigt, dass das Liskovsche Substitutionsprinzip eine der wichtigsten Regeln im objektorientierten Softwaredesign ist. Sehr viele Probleme in realen PHP-Projekten entstehen genau durch seine Nichtbeachtung. Außerdem hat das L-Prinzip natürlich Wechselwirkungen mit den beiden anderen bereits vorgestellten, Prinzipien. Sie tun also gut daran, in Zukunft bei jeder Ableitung einer Klasse und bei jeder Interfaceimplementierung an Frau Liskov und ihr Substitutionsprinzip zu denken.

Tobias Schlitt ist Diplominformatiker (Uni) und arbeitet seit 1999 in professionellen Webprojekten auf Basis von PHP. Als Open-Source-Enthusiast beteiligt er sich an verschiedenen Communityprojekten. Tobias ist Mitgründer der Qafoo GmbH (http://qafoo.com), die Entwicklungsteams dabei unterstützt, hochqualitative Websoftware zu entwickeln. Dazu zählen unter anderem Consulting und Training zu Qualitätssicherungsprozessen und professionellem Software-Engineering.
Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -