Warum sollte eine Klasse nicht "abstrakt" oder "endgültig / versiegelt" sein?

29

Nach mehr als 10 Jahren Java / C # -Programmierung erstelle ich entweder:

  • Abstrakte Klassen : Vertrag soll nicht wie besehen instanziiert werden.
  • final / sealed classes : Implementierung, die nicht als Basisklasse für etwas anderes gedacht ist.

Ich kann mir keine Situation vorstellen, in der eine einfache "Klasse" (dh weder abstrakt noch endgültig / versiegelt) "weise Programmierung" wäre.

Warum sollte eine Klasse nicht "abstrakt" oder "endgültig / versiegelt" sein?

BEARBEITEN

Dieser großartige Artikel erklärt meine Bedenken viel besser als ich kann.

Nicolas Repiquet
quelle
21
Weil es das heißt Open/Closed principle, nicht dasClosed Principle.
StuperUser
2
Was schreibst du beruflich? Es kann sehr wohl Ihre Meinung dazu beeinflussen.
Nun, ich kenne einige Plattformentwickler, die alles versiegeln, weil sie die Vererbungsschnittstelle minimieren wollen.
K ..
4
@StuperUser: Das ist kein Grund, es ist eine Platitude. Der OP fragt nicht, was das Platitude ist, er fragt warum . Wenn hinter einem Prinzip kein Grund steckt, gibt es keinen Grund, darauf zu achten.
Michael Shaw
1
Ich frage mich, wie viele UI-Frameworks durch Ändern der Window-Klasse in Sealed beschädigt würden.
Reactgular

Antworten:

46

Ironischerweise finde ich das Gegenteil: Die Verwendung von abstrakten Klassen ist eher die Ausnahme als die Regel, und ich neige dazu, finale / versiegelte Klassen zu missbilligen.

Schnittstellen sind ein typischer Design-by-Contract-Mechanismus, da Sie keine Interna angeben - Sie machen sich keine Sorgen darüber. Es ermöglicht, dass jede Ausführung dieses Vertrags unabhängig ist. Dies ist in vielen Bereichen der Schlüssel. Wenn Sie beispielsweise ein ORM erstellen, ist es sehr wichtig, dass Sie eine Abfrage auf einheitliche Weise an die Datenbank übergeben können, die Implementierungen können jedoch sehr unterschiedlich sein. Wenn Sie zu diesem Zweck abstrakte Klassen verwenden, wird die Verdrahtung in Komponenten durchgeführt, die möglicherweise für alle Implementierungen gelten oder nicht.

Für endgültige / versiegelte Klassen kann ich nur dann eine Entschuldigung finden, wenn es tatsächlich gefährlich ist, das Überschreiben zuzulassen - vielleicht ein Verschlüsselungsalgorithmus oder so. Ansonsten wissen Sie nie, wann Sie die Funktionalität aus lokalen Gründen erweitern möchten. Das Versiegeln einer Klasse schränkt Ihre Optionen für Gewinne ein, die in den meisten Szenarien nicht vorhanden sind. Es ist weitaus flexibler, Ihre Klassen so zu schreiben, dass sie später erweitert werden können.

Diese letztere Ansicht wurde für mich durch die Arbeit mit Komponenten von Drittanbietern gefestigt, die Klassen versiegelten, wodurch eine Integration verhindert wurde, die das Leben viel einfacher gemacht hätte.

Michael
quelle
17
+1 für den letzten Absatz. Ich habe häufig festgestellt, dass zu viel Kapselung in Bibliotheken von Drittanbietern (oder sogar in Standardbibliotheksklassen!) Eine größere Schmerzursache ist als zu wenig Kapselung.
Mason Wheeler
5
Das übliche Argument für das Versiegeln von Klassen ist, dass Sie nicht vorhersagen können, auf welche Weise ein Client Ihre Klasse möglicherweise überschreiben könnte, und dass Sie daher keine Garantien für ihr Verhalten abgeben können. Siehe blogs.msdn.com/b/ericlippert/archive/2004/01/22/…
Robert Harvey
12
@RobertHarvey Ich kenne das Argument und es klingt theoretisch großartig. Sie als Objektdesigner können nicht vorhersehen, wie die Leute Ihre Klassen erweitern können - genau deshalb sollten sie nicht versiegelt werden. Sie können nicht alles unterstützen - gut. Nicht. Aber nehmen Sie auch keine Optionen mit.
Michael
6
@RobertHarvey sind das nicht nur Leute, die LSP brechen und bekommen, was sie verdienen?
StuperUser
2
Ich bin auch kein großer Fan von versiegelten Klassen, aber ich konnte sehen, warum einige Unternehmen wie Microsoft (die häufig Dinge unterstützen müssen, die sie nicht selbst brechen) sie attraktiv finden. Benutzer eines Frameworks müssen nicht unbedingt über Kenntnisse der Interna einer Klasse verfügen.
Robert Harvey
11

Das ist ein großartiger Artikel von Eric Lippert, aber ich denke nicht, dass er Ihre Sichtweise unterstützt.

Er argumentiert, dass alle Klassen, die für die Verwendung durch andere exponiert sind, versiegelt oder auf andere Weise nicht dehnbar gemacht werden sollten.

Sie gehen davon aus, dass alle Klassen abstrakt oder versiegelt sein sollten.

Großer Unterschied.

ELs Artikel sagt nichts über die (vermutlich vielen) Klassen aus, die von seinem Team produziert wurden und von denen Sie und ich nichts wissen. Im Allgemeinen sind die öffentlich zugänglichen Klassen in einem Framework nur eine Teilmenge aller Klassen, die an der Implementierung dieses Frameworks beteiligt sind.

Martin
quelle
Sie können dasselbe Argument jedoch auf Klassen anwenden, die nicht Teil einer öffentlichen Schnittstelle sind. Einer der Hauptgründe für das Finale einer Klasse ist, dass dies als Sicherheitsmaßnahme dient, um schlampigen Code zu verhindern. Dieses Argument gilt ebenso für jede Codebasis, die von verschiedenen Entwicklern im Laufe der Zeit gemeinsam genutzt wird, oder auch, wenn Sie der einzige Entwickler sind. Es schützt nur die Semantik des Codes.
DPM
Ich denke, dass die Idee, dass Klassen abstrakt oder versiegelt sein sollten, Teil eines umfassenderen Prinzips ist, nämlich, dass man die Verwendung von Variablen instanziierbarer Klassentypen vermeiden sollte. Eine solche Vermeidung ermöglicht es, einen Typ zu erstellen, der von den Verbrauchern eines bestimmten Typs verwendet werden kann, ohne dass alle privaten Mitglieder davon geerbt werden müssen. Leider funktionieren solche Designs nicht wirklich mit der Public-Konstructor-Syntax. Stattdessen müsste Code durch so new List<Foo>()etwas wie ersetzt werden List<Foo>.CreateMutable().
Supercat
5

Aus Java-Sicht denke ich, dass die Abschlussklassen nicht so schlau sind, wie es scheint.

Viele Tools (insbesondere AOP, JPA usw.) arbeiten mit Wartezeiten, sodass sie Ihre Klassen erweitern müssen. Die andere Möglichkeit besteht darin, Delegaten (nicht die .NET-Delegaten) zu erstellen und alles an die ursprüngliche Klasse zu delegieren. Dies wäre viel umständlicher als das Erweitern der Benutzerklassen.

Uwe Plonus
quelle
5

Zwei häufige Fälle, in denen Sie nicht versiegelte Vanille-Klassen benötigen :

  1. Technisch: Wenn Sie eine Hierarchie mit mehr als zwei Ebenen haben und etwas in der Mitte instanziieren möchten.

  2. Prinzip: Manchmal ist es wünschenswert, explizite Klassen zu schreiben , die sicher erweitert werden können . (Dies passiert häufig, wenn Sie eine API wie Eric Lippert schreiben oder wenn Sie in einem Team an einem großen Projekt arbeiten.) Manchmal möchten Sie eine Klasse schreiben, die für sich alleine funktioniert, aber die Erweiterbarkeit berücksichtigt.

Eric Lippert Gedanken über Dichtungs macht Sinn, aber er gibt auch zu, dass sie tun Entwurf für Erweiterbarkeit durch die Klasse „offen“ zu verlassen.

Ja, viele Klassen sind in der BCL versiegelt, aber eine große Anzahl von Klassen nicht. Sie können auf alle möglichen Arten erweitert werden. Ein Beispiel dafür ist Windows Forms, bei dem Sie nahezu allen Controlüber die Vererbung Daten oder Verhalten hinzufügen können . Natürlich hätte dies auch auf andere Weise geschehen können (Dekorationsmuster, verschiedene Arten von Kompositionen usw.), aber die Vererbung funktioniert auch sehr gut.

Zwei .NET-spezifische Hinweise:

  1. In den meisten Fällen sind Siegelklassen häufig nicht sicherheitskritisch, da Vererbungsunternehmen virtualmit Ausnahme expliziter Schnittstellenimplementierungen nicht mit Ihrer Nichtfunktionalität in Konflikt geraten können .
  2. Manchmal ist es eine geeignete Alternative, den Konstruktor internalzu erstellen, anstatt die Klasse zu versiegeln. Dadurch kann die Klasse innerhalb der Codebasis, aber nicht außerhalb der Codebasis vererbt werden.
Kevin McCormick
quelle
4

Ich glaube, der wahre Grund, warum viele Leute der Meinung sind, Klassen sollten final/ sealedsind, dass die meisten nicht abstrakten erweiterbaren Klassen nicht richtig dokumentiert sind .

Lassen Sie mich näher darauf eingehen. Aus der Ferne gibt es unter einigen Programmierern die Ansicht, dass die Vererbung als Werkzeug in OOP häufig überbeansprucht und missbraucht wird. Wir haben alle das Liskov-Substitutionsprinzip gelesen, obwohl dies uns nicht davon abgehalten hat, es hunderte (vielleicht sogar tausende) Male zu verletzen.

Die Sache ist, dass Programmierer gerne Code wiederverwenden. Auch wenn es keine so gute Idee ist. Und Vererbung ist ein Schlüsselwerkzeug für dieses "erneute Übertragen" von Code. Zurück zur letzten / versiegelten Frage.

Die richtige Dokumentation für eine endgültige / versiegelte Klasse ist relativ klein: Sie beschreiben, was jede Methode tut, was die Argumente sind, den Rückgabewert usw. Alle üblichen Dinge.

Wenn Sie jedoch eine erweiterbare Klasse ordnungsgemäß dokumentieren , müssen Sie mindestens Folgendes einschließen :

  • Abhängigkeiten zwischen Methoden (welche Methode ruft welche auf usw.)
  • Abhängigkeiten von lokalen Variablen
  • Interne Verträge, die die erweiterte Klasse einhalten sollte
  • Aufruf der Konvention für jede Methode (z. B. Wenn Sie sie überschreiben, rufen Sie die superImplementierung auf. Rufen Sie sie am Anfang der Methode oder am Ende auf. Denken Sie an Konstruktor vs. Destruktor.)
  • ...

Diese sind einfach von der Spitze meines Kopfes. Und ich kann Ihnen ein Beispiel dafür geben, warum all dies wichtig ist, und wenn Sie es überspringen, wird dies eine erweiterte Klasse durcheinander bringen.

Überlegen Sie nun, wie viel Dokumentationsaufwand in die ordnungsgemäße Dokumentation all dieser Dinge investiert werden sollte. Ich glaube, dass eine Klasse mit 7-8 Methoden (die in einer idealisierten Welt zu viel, in der Realität jedoch zu wenig ist) genauso gut eine Dokumentation mit 5 Seiten nur Text enthalten kann. Stattdessen schleichen wir uns auf halbem Weg aus und versiegeln die Klasse nicht, damit andere sie nutzen können, dokumentieren sie jedoch auch nicht richtig, da dies sehr viel Zeit in Anspruch nimmt (und Sie wissen, dass dies möglicherweise der Fall ist) niemals verlängert werden, also warum sich die Mühe machen?).

Wenn Sie eine Klasse entwerfen, verspüren Sie möglicherweise die Versuchung, sie zu versiegeln, damit die Leute sie nicht auf eine Weise verwenden können, die Sie nicht vorausgesehen haben (und auf die Sie vorbereitet sind). Wenn Sie andererseits den Code eines anderen Benutzers verwenden, gibt es manchmal über die öffentliche API keinen sichtbaren Grund für die Endgültigkeit der Klasse, und Sie könnten denken: "Verdammt, das hat mich nur 30 Minuten bei der Suche nach einer Problemumgehung gekostet."

Ich denke, einige der Elemente einer Lösung sind:

  • Erstens , um sicherzustellen , erstreckt , ist eine gute Idee , wenn Sie ein Client des Codes sind, und wirklich Komposition der Vererbung zu begünstigen.
  • Zweitens , um das Handbuch zu lesen in seiner Gesamtheit (wieder als Client) , um sicherzustellen , sind mit Blick auf Sie nicht etwas , das erwähnt wird.
  • Drittens, wenn Sie einen Code schreiben, den der Client verwenden wird, schreiben Sie die richtige Dokumentation für den Code (ja, den langen Weg). Als positives Beispiel kann ich Apples iOS-Dokumente geben. Sie reichen für den Benutzer nicht aus, um seine Klassen immer ordnungsgemäß zu erweitern, enthalten jedoch zumindest einige Informationen zur Vererbung. Das ist mehr als ich für die meisten APIs sagen kann.
  • Viertens versuchen Sie, Ihre eigene Klasse zu erweitern, um sicherzustellen, dass es funktioniert. Ich bin ein großer Befürworter der Aufnahme vieler Beispiele und Tests in APIs, und wenn Sie einen Test durchführen, können Sie auch Vererbungsketten testen: Sie sind schließlich Teil Ihres Vertrags!
  • Fünftens: Zeigen Sie in Zweifelsfällen an, dass die Klasse nicht erweitert werden soll und dass dies eine schlechte Idee ist. Geben Sie an, dass Sie für eine solche unbeabsichtigte Verwendung nicht zur Rechenschaft gezogen werden sollten, versiegeln Sie die Klasse jedoch nicht. Dies gilt natürlich nicht für Fälle, in denen die Klasse zu 100% versiegelt sein sollte.
  • Wenn Sie eine Klasse versiegeln, stellen Sie schließlich eine Schnittstelle als Zwischenhaken bereit, damit der Client seine eigene modifizierte Version der Klasse "umschreiben" und Ihre "versiegelte" Klasse umgehen kann. Auf diese Weise kann er die versiegelte Klasse durch seine Implementierung ersetzen. Dies sollte nun offensichtlich sein, da es sich um eine lose Kupplung in ihrer einfachsten Form handelt, die jedoch noch eine Erwähnung wert ist.

Es lohnt sich auch die folgende „philosophische“ Frage zu erwähnen: Ist , ob eine Klasse sealed/ finalTeil des Vertrages für die Klasse oder eine Implementierung Detail? Jetzt möchte ich nicht dorthin gehen, aber eine Antwort darauf sollte auch Ihre Entscheidung beeinflussen, ob Sie eine Klasse besiegeln oder nicht.

K.Steff
quelle
3

Eine Klasse sollte weder endgültig / versiegelt noch abstrakt sein, wenn:

  • Es ist für sich genommen nützlich, dh es ist vorteilhaft, Instanzen dieser Klasse zu haben.
  • Es ist vorteilhaft, wenn diese Klasse die Unterklasse / Basisklasse anderer Klassen ist.

Nehmen Sie zum Beispiel die ObservableCollection<T>Klasse in C # . Es muss nur das Auslösen von Ereignissen zu den normalen Operationen von a hinzugefügt werden Collection<T>, weshalb es Unterklassen bildet Collection<T>. Collection<T>ist eine tragfähige Klasse für sich, und so ist es ObservableCollection<T>.

FischkorbGordo
quelle
Wenn ich dafür verantwortlich wäre, würde ich wahrscheinlich CollectionBase(Zusammenfassung), Collection : CollectionBase(versiegelt), ObservableCollection : CollectionBase(versiegelt) tun . Wenn Sie sich Collection <T> genau ansehen , werden Sie feststellen, dass es sich um eine halbherzige abstrakte Klasse handelt.
Nicolas Repiquet
2
Welchen Vorteil haben Sie, wenn Sie drei statt nur zwei Klassen haben? Wie ist Collection<T>eine "halbherzige abstrakte Klasse"?
FishBasketGordo
Collection<T>Enthüllt viele Innereien durch geschützte Methoden und Eigenschaften und ist eindeutig als Basisklasse für spezialisierte Sammlungen gedacht. Es ist jedoch nicht klar, wo Sie Ihren Code ablegen sollen, da es keine abstrakten Methoden zum Implementieren gibt. ObservableCollectionerbt von Collectionund ist nicht versiegelt, sodass Sie wieder von ihm erben können. Und Sie können auf die Itemsgeschützte Eigenschaft zugreifen , sodass Sie Elemente in die Sammlung aufnehmen können, ohne Ereignisse auslösen zu müssen.
Nicolas Repiquet
@NicolasRepiquet: In vielen Sprachen und Frameworks erfordert die normale idiomatische Methode zum Erstellen eines Objekts, dass der Typ der Variablen, die das Objekt enthält, dem Typ der erstellten Instanz entspricht. In vielen Fällen würde die ideale Verwendung darin bestehen, Verweise auf einen abstrakten Typ weiterzugeben, aber dies würde eine Menge Code dazu zwingen, einen Typ für Variablen und Parameter und einen anderen konkreten Typ beim Aufrufen von Konstruktoren zu verwenden. Kaum unmöglich, aber etwas umständlich.
Supercat
3

Das Problem mit finalen / versiegelten Klassen ist, dass sie versuchen, ein Problem zu lösen, das noch nicht passiert ist. Dies ist nur dann nützlich, wenn das Problem besteht, aber frustrierend, weil ein Dritter eine Einschränkung auferlegt hat. Dichtungsklasse löst selten ein aktuelles Problem, was es schwierig macht, seine Nützlichkeit zu argumentieren.

Es gibt Fälle, in denen eine Klasse versiegelt werden sollte. Zum Beispiel; Die Klasse verwaltet die zugewiesenen Ressourcen / den zugewiesenen Speicher so, dass sie nicht vorhersagen kann, wie zukünftige Änderungen diese Verwaltung verändern könnten.

Im Laufe der Jahre habe ich festgestellt, dass Kapselung, Rückrufe und Ereignisse weitaus flexibler und nützlicher sind als abstrahierte Klassen. Ich sehe viel zu viel Code mit einer großen Hierarchie von Klassen, in denen die Kapselung und Ereignisse dem Entwickler das Leben einfacher gemacht hätten.

Reactgular
quelle
1
Final Sealed Stuff in Software löst das Problem "Ich habe das Bedürfnis, zukünftigen Betreuern dieses Codes meinen Willen aufzuzwingen".
Kaz
Wurde nicht final / seal etwas zu OOP hinzugefügt, weil ich mich nicht daran erinnere, wie es war, als ich jünger war. Es scheint ein nachdenkliches Feature zu sein.
Reactgular
Das Finalisieren war eine Toolchain-Prozedur in OOP-Systemen, bevor OOP mit Sprachen wie C ++ und Java in Konflikt geriet. Programmierer arbeiten in Smalltalk, Lisp mit maximaler Flexibilität: Alles kann erweitert werden, neue Methoden werden ständig hinzugefügt. Anschließend wird das erstellte Abbild des Systems optimiert: Es wird davon ausgegangen, dass das System von den Endbenutzern nicht erweitert wird. Dies bedeutet, dass der Methodenversand optimiert werden kann, indem eine Bestandsaufnahme durchgeführt wird, und Klassen existieren jetzt .
Kaz
Ich denke nicht, dass das dasselbe ist, da dies nur eine Optimierungsfunktion ist. Ich erinnere mich nicht, dass ich in allen Java-Versionen versiegelt war, aber ich könnte mich irren, da ich es nicht oft benutze.
Reactgular
Nur weil es sich in einigen Deklarationen manifestiert, die Sie in den Code einfügen müssen, heißt das nicht, dass es nicht dasselbe ist.
Kaz
2

"Siegeln" oder "Finalisieren" in Objektsystemen ermöglicht bestimmte Optimierungen, da die vollständige Versandgrafik bekannt ist.

Das heißt, wir machen es schwierig, das System in etwas anderes zu verwandeln, als Kompromiss für die Leistung. (Das ist die Essenz der meisten Optimierungen.)

Ansonsten ist es ein Verlust. Systeme sollten standardmäßig geöffnet und erweiterbar sein. Es sollte einfach sein, allen Klassen neue Methoden hinzuzufügen und beliebig zu erweitern.

Wir erhalten heute keine neuen Funktionen, indem wir Maßnahmen ergreifen, um zukünftige Erweiterungen zu verhindern.

Wenn wir es also zum Zwecke der Vorbeugung selbst tun, versuchen wir, das Leben zukünftiger Instandhalter zu kontrollieren. Es geht um das Ego. "Auch wenn ich hier nicht mehr arbeite, wird dieser Code auf meine Art beibehalten, verdammt!"

Kaz
quelle
1

Testklassen scheinen in den Sinn zu kommen. Hierbei handelt es sich um Klassen, die automatisch oder "nach Belieben" aufgerufen werden, je nachdem, was der Programmierer / Tester zu erreichen versucht. Ich bin mir nicht sicher, ob ich jemals eine abgeschlossene Klasse von Tests gesehen oder gehört habe, die privat ist.

joshin4colours
quelle
Wovon redest du Inwiefern dürfen Testklassen nicht endgültig / versiegelt / abstrakt sein?
1

Die Zeit, in der Sie eine Klasse in Betracht ziehen müssen, die erweitert werden soll, ist die Zeit, in der Sie eine echte Planung für die Zukunft durchführen. Lassen Sie mich Ihnen ein reales Beispiel aus meiner Arbeit geben.

Ich verbringe viel Zeit damit, Schnittstellen-Tools zwischen unserem Hauptprodukt und externen Systemen zu schreiben. Wenn wir einen neuen Verkauf abschließen, besteht eine große Komponente aus einer Reihe von Exporteuren, die in regelmäßigen Abständen ausgeführt werden und Datendateien mit den Ereignissen an diesem Tag erstellen. Diese Datendateien werden dann vom System des Kunden verwendet.

Dies ist eine hervorragende Gelegenheit, den Unterricht zu erweitern.

Ich habe eine Export-Klasse, die die Basisklasse jedes Exporteurs ist. Es kann eine Verbindung zur Datenbank herstellen, herausfinden, wo sie zuletzt ausgeführt wurde, und Archive der von ihr generierten Datendateien erstellen. Es bietet auch die Verwaltung von Eigenschaftendateien, die Protokollierung und einige einfache Ausnahmebehandlungen.

Darüber hinaus habe ich einen anderen Exporteur, der mit den einzelnen Datentypen arbeitet. Möglicherweise gibt es Benutzeraktivitäten, Transaktionsdaten, Cash-Management-Daten usw.

Auf diesen Stapel lege ich eine kundenspezifische Ebene, die die vom Kunden benötigte Datendateistruktur implementiert.

Auf diese Weise ändert sich der Basis-Exporteur sehr selten. Die wichtigsten Exportprogramme für Datentypen ändern sich manchmal, aber selten, und normalerweise nur, um Datenbankschemaänderungen zu verarbeiten, die ohnehin an alle Kunden weitergegeben werden sollten. Die einzige Arbeit, die ich jemals für jeden Kunden erledigen muss, ist der Teil des Codes, der für diesen Kunden spezifisch ist. Eine perfekte Welt!

Die Struktur sieht also so aus:

Base
 Function1
  Customer1
  Customer2
 Function2
 ...

Mein wichtigster Punkt ist, dass ich durch die Architektur des Codes auf diese Weise die Vererbung hauptsächlich für die Wiederverwendung von Code nutzen kann.

Ich muss sagen, ich kann mir keinen Grund vorstellen, über drei Schichten hinauszugehen.

Ich habe oft zwei Ebenen verwendet, um zum Beispiel eine gemeinsame TableKlasse zu haben, die Abfragen von Datenbanktabellen implementiert, während Unterklassen Tabledie spezifischen Details jeder Tabelle implementieren, indem sie ein verwenden enum, um die Felder zu definieren. Das enumImplementieren einer in der TableKlasse definierten Schnittstelle zuzulassen, ist in jeder Hinsicht sinnvoll.

OldCurmudgeon
quelle
1

Ich fand abstrakte Klassen nützlich, aber nicht immer notwendig, und versiegelte Klassen wurden zu einem Problem bei der Anwendung von Komponententests. Sie können eine versiegelte Klasse nicht verspotten oder stummschalten, es sei denn, Sie verwenden so etwas wie Teleriks Justmock.

Dan H
quelle
1
Dies ist ein guter Kommentar, der die Frage jedoch nicht direkt beantwortet. Bitte erwägen Sie, Ihre Gedanken hier zu erweitern.
1

Ich stimme Ihrer Ansicht zu. Ich denke, dass in Java Klassen standardmäßig als "final" deklariert werden sollten. Wenn Sie es nicht endgültig machen, bereiten Sie es speziell vor und dokumentieren Sie es für die Erweiterung.

Der Hauptgrund dafür ist, sicherzustellen, dass alle Instanzen Ihrer Klassen der Schnittstelle entsprechen, die Sie ursprünglich entworfen und dokumentiert haben. Andernfalls kann ein Entwickler, der Ihre Klassen verwendet, möglicherweise spröden und inkonsistenten Code erstellen und diesen an andere Entwickler / Projekte weitergeben, wodurch Objekte Ihrer Klassen nicht als vertrauenswürdig eingestuft werden.

Aus praktischer Sicht hat dies in der Tat einen Nachteil, da Clients der Benutzeroberfläche Ihrer Bibliothek keine Anpassungen vornehmen und Ihre Klassen auf flexiblere Weise verwenden können, als Sie es ursprünglich gedacht hatten.

Persönlich halte ich bei all dem schlechten Qualitätscode, der existiert (und da wir dies auf einer praktischeren Ebene diskutieren, würde ich behaupten, dass die Java-Entwicklung dafür anfälliger ist), diese Starrheit für einen geringen Preis, den wir für unsere Suche nach Einfachheit zahlen müssen Code pflegen.

DPM
quelle