{"id":314,"date":"2015-04-14T18:27:26","date_gmt":"2015-04-14T16:27:26","guid":{"rendered":"http:\/\/invidit.de\/blog\/?p=314"},"modified":"2015-09-06T09:53:33","modified_gmt":"2015-09-06T07:53:33","slug":"ist-wirklich-jeder-ersetzbar","status":"publish","type":"post","link":"https:\/\/invidit.de\/blog\/ist-wirklich-jeder-ersetzbar\/","title":{"rendered":"Ist wirklich jeder ersetzbar?"},"content":{"rendered":"<p>Hallo Spa\u00df-Coder.<\/p>\n<p>Wer von euch hat schon einmal eine abgeleitete Klasse (also Kindklasse) verwendet, und diese verhielt sich anders als die Ober(-\/Vater)klasse und damit anders, als ihr es erwartet habt? Oder ihr verarbeitet die Ausnahmen (Exception) einer Klasse alle sehr sorgf\u00e4ltig und nun kommt eine Kindklasse der bisherigen Implementierung daher und wirft pl\u00f6tzlich einen neuen Typ einer Ausnahme? Das war so nicht abgesprochen (oder erwartet)! Hier kommt das dritte Prinzip der SO<strong>L<\/strong>ID-Prinzipien ins Spiel.<\/p>\n<p>&nbsp;<\/p>\n<h2 id=\"firstHeading\" lang=\"de\">Liskovsches Substitutionsprinzip (LSP)<\/h2>\n<p>Die Definition des LSP von Barbara Liskov und Jeanette Wing wurde nach <a title=\"Wikipedia\" href=\"https:\/\/de.wikipedia.org\/wiki\/Liskovsches_Substitutionsprinzip\">Wikipedia<\/a> so formuliert:<\/p>\n<p>&#8222;Sei <em>q(x)<\/em> eine beweisbare Eigenschaft von Objekten <em>x<\/em> des Typs <em>T<\/em>. Dann soll <em>q(y)<\/em> f\u00fcr Objekte <em>y<\/em> des Typs <em>S<\/em> wahr sein, wobei <em>S<\/em> ein Untertyp von <em>T<\/em> ist.&#8220;<\/p>\n<p>Damit ist doch alles gesagt, oder?<\/p>\n<p>&nbsp;<\/p>\n<h2>Erl\u00e4uterung des Prinzips<\/h2>\n<p>Die oben genannten Definition mal anders formuliert sagt etwas aus wie:<\/p>\n<p>&#8222;Eine Kindklasse sollte sich so verhalten, dass es keine Probleme (oder unerwartetes Verhalten) gibt, wenn sie anstelle der Oberklasse verwendet wird.&#8220;<\/p>\n<p>Diese Aussage wird in den folgenden Regeln konkretisiert.<\/p>\n<p>1. In einer abgeleiteten Klasse d\u00fcrfen Methodenparameter generischer sein als die Parameter in der gleichen Methode der Oberklasse. Demnach darf in der Kindklasse als Methodenparameter eine Oberklasse des bisherigen Parameters verwendet werden. R\u00fcckgabewerte von Methoden d\u00fcrfen spezifischer sein als in der Oberklasse. Demnach darf in der abgeleiteten Klasse eine Kindklasse des bisherigen R\u00fcckgabewertes verwendet werden.<\/p>\n<p>Aufgrund der Ableitungshierarchie ist dies plausibel. Angenommen, ich tausche die Verwendung einer Klasse durch eine abgeleitete Kindklasse aus. W\u00fcrde diese in einer Methode nun eine Kindklasse des bisherigen Parameters erwarten, wird dies in den bisherigen Implementierungen nicht ber\u00fccksichtigt.<\/p>\n<p><span style=\"text-decoration: underline;\">Beispiel:<\/span> Ein <em>BMW<\/em> ist ein <em>Auto<\/em> ist ein <em>Fahrzeug<\/em>. Wenn ich nun ein <em>BMW<\/em> anstelle eines <em>Auto<\/em>s erwarte, schl\u00e4gt der Aufruf fehlt, da ein <em>Auto<\/em> kein <em>BMW<\/em> ist &#8211; sondern nur andersherum.<br \/>\n2. In der abgeleiteten Klasse d\u00fcrfen keine neuen Exception-Typen geworfen werden. Werden alle bisherigen Ausnahmetypen fein s\u00e4uberlich abgefangen wird der Code zur Laufzeit kaputt gehen, wenn nun in einer Kindklasse ein neuer Typ einer Ausnahme geworfen wird, da dieser Typ nicht abgefangen wird. Zul\u00e4ssig ist hingegen, ein Ausnahmetyp von einer verwendeten Ausnahme abzuleiten, da diese auch immer vom Typ der Oberklasse ist und damit die Ausnahmebehandlung vollst\u00e4ndig bleibt.<\/p>\n<p>&nbsp;<\/p>\n<p>3. Vorbedingungen d\u00fcrfen in der Kindklasse nicht verst\u00e4rkt werden. So d\u00fcrfen keine neuen Dinge erwartet werden, die eine bisherige Verwendung nicht erf\u00fcllt. Dies w\u00fcrde alle bisherigen Verwendungen der Oberklasse beim Austausch gegen eine Kindklasse kaputt machen. Nachbedingungen d\u00fcrfen in der Kindklasse nicht geschw\u00e4cht werden. Dies bedeutet, dass eine Kindklasse nicht weniger als ihre Oberklasse zur\u00fcckgeben darf, da hier bei der Verwendung bestimmte Erwartungen an die R\u00fcckgabewerte gekn\u00fcpft sind.<\/p>\n<p>&nbsp;<\/p>\n<p>Zu diesem Prinzip gibt es im gesamten Netz nur ein Beispiel: Quadrat und Rechteck. Quadrat darf nicht von Rechteck ableiten. Warum? Beide haben doch vier Seiten. Ein Quadrat hat jedoch eine Einschr\u00e4nkung gegen\u00fcber einem Rechteck &#8211; es haben n\u00e4mlich alle Seiten dieselbe L\u00e4nge, was bei einem Quadrat bei beiden Seitenpaaren unabh\u00e4ngig voneinander der Fall ist. Wenn nun eine Verwendung von Quadrat erwartet, dass H\u00f6he und Breite unabh\u00e4ngig von einander gesetzt werden k\u00f6nnen, bricht dies das Prinzip &#8211; ein Quadrat hat nur eine und nur genau eine Kantenl\u00e4nge.<\/p>\n<p>&nbsp;<\/p>\n<h2>Unsere Erfahrung mit dem LSP<\/h2>\n<p>Mir ist gerade nicht bekannt, dass wir das Liskovsche Substitutionsprinzip bewusst eingesetzt haben. F\u00fcr mich ist der zweite genannte Punkt bez\u00fcglich der Ausnahmen interessant. Hier ein Bewusstsein daf\u00fcr zu entwickeln, dass ich bei abgeleiteten Klassen keine neuen Ausnahmetypen definiere und werfe, sondern &#8211; falls erforderlich &#8211; immer nur bestehende Ausnahmetypen ableite und damit konkretisiere.<\/p>\n<p>&nbsp;<\/p>\n<h2>Zusammenfassung<\/h2>\n<p>Bei allen SOLID-Prinzipien baut ein Prinzip immer auf den vorhergehenden Prinzipien auf. Das LSP mag m\u00f6glicherweise auf Anhieb wenig Relevanz f\u00fcr eure t\u00e4gliche Programmierpraxis haben, aber auch hier geht es um die Austauschbarkeit von Komponenten, die genau eine Verantwortlichkeit haben. Wenn ihr Erfahrungen mit diesem Prinzip gemacht habt, lasst uns teilhaben und schreibt einen Kommentar dazu.<\/p>\n<p>&nbsp;<\/p>\n<p>Viel Spa\u00df beim Anwenden.<\/p>\n<p>Eure Spa\u00df-Coder.<br \/>\nDieser Artikel basiert neben unseren Erfahrungen auf verschiedenen Internetquellen, unter anderem diesem Video bei Youtube:<\/p>\n<ul>\n<li>\n<p class=\"yt watch-title-container\"><span id=\"eow-title\" class=\"watch-title \" dir=\"ltr\" title=\"Applying S.O.L.I.D. Principles in Microsoft .NET\/C#\">Applying S.O.L.I.D. Principles in Microsoft .NET\/C# (<a title=\"https:\/\/youtu.be\/Whhi1C2PpaA\" href=\"https:\/\/youtu.be\/Whhi1C2PpaA\">https:\/\/youtu.be\/Whhi1C2PpaA<\/a>)<br \/>\n<\/span><\/p>\n<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Hallo Spa\u00df-Coder. Wer von euch hat schon einmal eine abgeleitete Klasse (also Kindklasse) verwendet, und diese verhielt sich anders als die Ober(-\/Vater)klasse und damit anders, als ihr es erwartet habt? Oder ihr verarbeitet die Ausnahmen (Exception) einer Klasse alle sehr sorgf\u00e4ltig und nun kommt eine Kindklasse der bisherigen Implementierung daher und wirft pl\u00f6tzlich einen neuen [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":330,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[90],"tags":[46,42,45,44,43],"_links":{"self":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/314"}],"collection":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/comments?post=314"}],"version-history":[{"count":13,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/314\/revisions"}],"predecessor-version":[{"id":329,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/314\/revisions\/329"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/media\/330"}],"wp:attachment":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/media?parent=314"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/categories?post=314"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/tags?post=314"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}