Beim Auto gab es sehr viele Erfahrungswerte, die zu diesen Sicherheitsvorschriften geführt haben. Wenn ich allein was programmiere, gibt es nur meine Erfahrung.
Mit Vererbung ist es doch das selbe, auch da gibt es unzählige schlechte Erfahrungen, nicht nur meine persönlichen. Viele Software-Architekturen helfen sich mit Daumenregeln, die das schlimmste vermeiden sollen. Die lauten da etwa so: Vererbungshierarchien sollten niemals tiefer als zwei Ebenen reichen. Es sollte nur von abstraken Klassen geerbt werden. Vererbung sollte nicht über Modul-Grenzen hinaus stattfinden… Andere Programmiersprachen verzichten sogar ganz auf Vererbung.
Wenn du ihn ebenbürtig ansiehst, müsste doch ein Hinweis statt einer Absperrung reichen.
Nein, gerade weil meine NutzerInnen wertschätze, möchte ich nicht ihre Zeit und andere ökonomische Ressourcen verschwenden. Genau das mache ich aber, wenn ich von ihnen unnötigerweise verlange sich zu einer Design-Entscheidung zu positionieren, die ich schon während der Schnittstellen-Festlegung hätte treffen können. Aus dem selben Grund benutze ich auch bevorzugt statisch typsisierte Programmiersprachen. Natürlich enge ich damit die Lösungsmöglichkeiten ein, aber vor allem schließe ich kränkelnde Lösungsmöglichkeiten aus. Lösungsmöglichkeiten einschränken heißt ja im übrigen nicht automatisch, dass damit auch bestimmte Problemstellung nicht mehr lösbar wären. Der Weg zum Ziel ist lediglich besser ausgeleuchtet und Irrwege teilweise versperrt.
Ich sehe das umgekehrt: Wenn ich eine Klasse für Vererbung öffne, dann füge ich der Schnittstelle eine gehörige Portion Komplexität hinzu, sie gewinnt aber nicht an Ausdrucksstärke.
Etwas, das nicht vorhanden ist, fügt Komplexität hinzu, nur weil ich eine Verhinderung weglasse?
Schöner doppelter Negativ 😂 Aber ja, in meinen Augen tut es das. Maximale Flexibilität und minimale Komplexität sind Ziele, die sich nicht immer miteinander vereinbaren lassen.
Nur durch das Zulassen von Vererbung werden ja nicht auf magische Weise neue Anwendungsfälle abgedeckt.
Das nicht, aber wenn es welche gibt, die du dir nur nicht vorstellen kannst, hast du sie zumindest mal aktiv verhindert.
Zwei Dinge hierzu: Erstens, halte ich es für gutes Design, wenn Schnittstellen möglichst orthogonale Features anbieten und nicht mehrere Lösungswege für ein und das selbe Problem. Mehrere Lösungswege können sinnvoll sein, wenn sie verschiedene Trade-Offs ermöglichen. Zwei Lösungsmöglichkeiten mit den selben Trade-Offs, aber einer davon mit erheblichen Nachteilen, ist kein gutes Design.
Zweitens: Wie oben schon gesagt, bedeutet Lösungswege einzuschränken nicht zwingenderweise auch Probleminstanzen auszugrenzen. Im Falle von Subclass-Polymorphismus kann man immer auch Ad-Hoc-Polymorphimus als Supplement einsetzen, also Interfaces anstelle von Vererbungshierarchien.
Wenn ich also eine Klasse schon öffne, und die gesteigerte Komplexität in Kauf nehme, dann sollte ich auch einen konkreten Anwendungsfall damit abdecken wollen. Anderenfalls, YAGNI.
YAGNI heißt für mich, dass man etwas nicht einzubauen braucht, für das man noch keine Verwendung absehen kann. Ich sehe darin nicht, dass man derzeit unbekannte Anwendungsfälle aktiv verhindern muss.
Dann mal ein Gedankenspiel, angenommen PHPs Klassen wären standardmäßig immer final
und um Vererbung zu ermöglichen müsste man die Klasse als inheritable
deklarieren. Würdest du das dann immer tun, auch wenn du keine Verwendung dafür absehen kannst?