Tach!
Selbst wenn ich sie als final ansehe, würde ich dieses Schlüsselwort nicht setzen wollen. Was stört es mich denn, wenn ein Verwender davon erbt, wenn das unbedingt gewünscht ist? Aus welchem Grunde muss ich das aktiv verhindern?
Zum einen schütze ich die Nutzer und Nutzerinnen der Klasse vor einer Verwendung, die ich als Autor nicht beabsichtigt habe und die auf seiner bzw. ihrer Seite früher oder später für Frustration sorgen wird: Methodenüberladung kann dafür sorgen, dass Implementierungs-Details der Elternklasse die Kindklasse auf unvorhersehbare Weise beeinflussen.
Ich wähle für die Antwort mal eine paar sehr herbe Worte, formuliert als Angriff, Übertreibung als Stilmittel. Lass sie nicht bis zum Gemüt vordringen, das könnte sich darüber aufregen, was ich aber nicht beabsichtige. Los geht's: Wofür hältst du dich eigentlich, dass du meinst den Verwender vor etwas beschützen zu müssen? Meinst du die Verwender sind nicht gut genug im Programmieren und Code-Verstehen, die potentiellen Auswirkungen zu erkennen? Warum willst du sie davon abhalten, dass sie sich ins Knie schießen, wenn sie es denn unbedingt wollen? Du hast den Code geschrieben, du hast ihn freigegeben, das war's nun. Er ist nicht mehr dein Kind, wenn ein Clone davon anderswo läuft. Lass es laufen.</ende>
Gehen wir zum Beispiel von einer Klasse A
mit zwei öffentlichen Methoden aus:
[...]
Klasse B
erbt von A
und überschreibt die Methode bar
:
[...]
Frage, zu welchem Wert evaluiert die zweite Zeile in folgendem Code?
[...]
Wenn A aber intern $this->bar() aufruft, dann führt das zu einer Exception.
Wenn es denn so wichtig ist, dass foo() unbeeinflusst vom Zustand von bar() korrekt arbeitet, dann kann man das eigentliche Geschehen auch in eine private Methode auszulagern versuchen, die dann von foo() und bar() aufgerufen wird. Wenn jemand das öffentliche bar() überschreibt, kann er das tun, ohne die Abhängigkeit zu beeinflussen.
Aber auch das ist nicht in jedem Falle hübsch. Ein paar mal schon hatte ich mir bei Verwendung des .NET-Frameworks gewünscht, eine Methode für eigene Zwecke zu verwenden, weil ich ein etwas anderes Verhalten brauchte, als die vorhandenen Methoden hergaben. Aber ich kam da nicht ran, weil das eigentliche Verhalten in einer private InternalFoo() Methode steckte, und alle verwendbaren Wege dahin irgendwas Zusätzliches machten, das ich nicht brauchte. Ich hätte die ganze Klasse ersetzen und umbauen müssen, um da ranzukommen. Aber dann hätte der Rest des Frameworks mir immer noch Objekte der alten Implementation ohne meine Ergänzungen übergeben. Natürlich kann es an meiner Unerfahrenheit gelegen haben, dass ich dieses Vorgehen als Lösung angesehen hatte. Mittlerweile habe ich den Eindruck, dass bei neuerem Code SOLIDer gearbeitet wird, und Funktionalität ausgelagert und einzeln verwendbar gemacht wird. Das steckt zwar mitunter in Namespaces mit Internal im Namen, aber ist meist doch für Außenstehende verwendbar.
Der Knackpunkt ist, dass man als Autor der Klasse B nicht wissen kann, wie sie sich verhalten wird, wenn nur eine einzige Methode überschrieben wird, außer man sieht sich die Implementierung der Elternklasse an.
Ja, das sollte man tun, wenn man etwas verwenden möchte. Wenn es die Dokumentation nicht hergibt, notfalls mit dem Blick in die Quellen oder mit dem Decompiler. PHP selbst ist da jedoch nicht ganz so zugänglich. .NET-Code lässt sich einfacher in lesbaren C#-Code rückübersetzen. Außerdem ist da vieles sowieso im Original C# gewesen und nicht artfremdes C wie bei PHP. Aber dein Code ist ja PHP, und wenn du ihn nicht verschleiert und verschlüsselt auslieferst, hat man da ja die Chance, ihn sich anzusehen.
Zum aderen sorge ich mit final
auch dafür, dass die Schnittstelle homogen genutzt wird, ich gebe dem Verwender eine klare Intention zu ihrem Gebrauch mit.
Jein. Das ist lediglich ein einzelnes Wort. Es muss nicht immer Intention hinter einem bestimmten Gebrauch stecken. Auch "haben wir schon immer so gemacht" kann der Grund sein. Man kann da nicht immer zweifelsfrei erkennen, was dahintersteckt. Ein erklärender Text in der Dokumentation, warum das da so wichtig ist, und vielleicht auch, was die Folgen sein könnten, ist da meist viel verständlicher. Gerade mit weniger Erfahrung sieht man nur die Tatsache, dass es final oder sealed ist, weiß aber vielleicht noch so nicht recht, warum man sowas macht.
Damals habe ich das Problem gelöst, indem ich mir den Code der Erweiterungen kopiert und so modifiziert habe, dass sie kompositionell zusammenarbeiteten, das war ein Aha-Moment für mich.
Das ist schön für dich, aber für dich war es ein Erkenntnisprozess. Einfach nur ein final dastehen zu haben, setzt diesen Prozess nicht in Gang. Es wirkt eher wie ein Verbotsschild, dessen Grund man sich vielleicht erschließen kann, vielleicht aber auch nicht.
Abgesehen davon, dass ich für mich noch keinen Anwendungsfall gefunden habe, wo ich eine solche Unveränderbarkeit als notwendig erachtet hätte, will ich dem nicht komplett die Nützlichkeit absprechen. Aber ich schreibe ja meist auch Endanwender-Code und so gut wie nie für die Öffentlichkeit bestimmte Librarys.
dedlfix.