Im fünften Wissenshäppchen geht es um das Liskov Substitution
Principle. Inhalt Das LSP ist das dritte der fünf SOLID-Prinzipien.
Es wurde 1987 von Barbara Liskov definiert, die ihm auch seinen
Namen gab: Functions that use pointers or references to base
classes must be able to use objects of derived classes without
knowing it. Man könnte sagen, dass dies quasi die Grundanforderung
ist, um Polymorphie zu ermöglichen. Denn überall dort, wo eine
Basisklasse oder ein Interface erwartet wird, müssen auch alle
abgeleiteten bzw. implementierenden Klassen erlaubt sein. Dies ist
in den heutigen Programmiersprachen auch der Fall und der Compiler
prüft dies (statisch). Aber das LSP geht noch einen Schritt weiter
und fordert auch zur Laufzeit ein korrektes Verhalten. Erklärung
Subklassen müssen sich so verhalten, wie es von ihren Basisklassen
erwartet wird. Dies gilt auch für Interfaces und ihre
Implementierungen. Statisch ist das kein Problem: Der Compiler
prüft, ob alle geforderten Methoden von den Subklassen bzw.
implementierenden Klassen angeboten werden. Die korrekte
Verhaltensweise der Implementierung in den Subklassen bzw.
implementierenden Klassen kann jedoch nicht statisch geprüft
werden. Hierfür ist der Entwickler zuständig, um das LSP nicht zu
verletzen. Wenn beispielsweise eine Subklasse in einer
überschriebenen Methode eine NotImplementedException wirft, anstatt
sie zu implementieren, wird das LSP verletzt. Aber auch unerwartete
Nebeneffekte der Implementierungen in überschriebenen Methoden
verletzten das LSP (Beispiel: Square erbt von Rectangle und setzt
beim Aufruf von setA() auch die Seite b auf den gleichen Wert).
Beispiel Die Basisklasse Employee gibt die Methode work_overtime()
vor, die damit in allen Subklassen (Manager und Developer) zur
Verfügung steht. Somit kann das Project gerade noch erfolgreich
fertiggestellt werden. class Employee def work_overtime puts
"Working in the evening and on weekends..." end end class Manager
< Employee end class Developer < Employee end class Project
def finish(employees) employees.each { |e| e.work_overtime }
"Project done" end end puts Project.new.finish([Manager.new,
Developer.new]) # Working in the evening and on weekends... #
Working in the evening and on weekends... # Project done Aber was
passiert, wenn sich der Developer zwar formell an den "Vertrag"
seiner Basisklasse hält, die Methode work_overtime() aber wie folgt
überschreibt? class Developer < Employee def work_overtime raise
"Sorry, I use XP and adhere to the 40 hour workweek" end end puts
Project.new.finish([Manager.new, Developer.new]) # Working in the
evening and on weekends... # Traceback (most recent call last): #
4: from ./LSP.rb:23:in `' # 3: from ./LSP.rb:18:in `finish' # 2:
from ./LSP.rb:18:in `each' # 1: from ./LSP.rb:18:in `block in
finish' # ./LSP.rb:12:in `work_overtime': Sorry, I use XP and
adhere to the 40 hour workweek (RuntimeError) Autsch! Das Project
ist wohl zum Scheitern verurteilt! ;-) Es hatte ja keine Ahnung,
dass man Developer nicht zu Überstunden verdonnern kann, denn das
geht ja schließlich bei allen Employees... Vorteile Der Code wird
deutlich besser nachvollziehbar, da keine unerwarteten Fehler zur
Laufzeit auftreten, obwohl laut Compiler alles ok ist. Klassen
verhalten sich so, wie man es von ihnen erwarten würde ("Principle
of Least Astonishment"), und überraschen den Entwickler nicht.
Nachteile Die Möglichkeiten der Vererbung werden ein wenig
eingeschränkt. Literaturempfehlungen Auch beim dritten
SOLID-Prinzip empfehle ich wieder Uncle Bobs Clean Code*. * Links
Permalink zu dieser Podcast-Episode RSS-Feed des Podcasts Liskov
Substitution Principle ArticleS.UncleBob.PrinciplesOfOod (Artikel
von Uncle Bob, Beispiel in C++) Liskovsches Substitutionsprinzip –
Wikipedia
Kommentare (0)
Melde dich an, um einen Kommentar zu schreiben.