Bei meiner ersten Implementierung zur Erweiterung des Java-Erfassungsframeworks war ich ziemlich überrascht, dass die Erfassungsschnittstelle als optional deklarierte Methoden enthält. Es wird erwartet, dass der Implementierer UnsupportedOperationExceptions auslöst, wenn dies nicht unterstützt wird. Dies fiel mir sofort als schlechte Wahl für das API-Design auf.
Nachdem er viel von Joshua Blochs exzellentem Buch "Effective Java" gelesen und später erfahren hatte, dass er möglicherweise für diese Entscheidungen verantwortlich ist, schien es nicht mit den in dem Buch vertretenen Prinzipien übereinzustimmen. Ich würde denken, zwei Schnittstellen zu deklarieren: Collection und MutableCollection, die Collection mit den "optionalen" Methoden erweitern, hätten zu viel besser wartbarem Client-Code geführt.
Es gibt eine ausgezeichnete Zusammenfassung der Probleme hier .
Gab es einen guten Grund, warum anstelle der Implementierung der beiden Schnittstellen optionale Methoden ausgewählt wurden?
quelle
Antworten:
Die FAQ liefert die Antwort. Kurz gesagt, sie sahen eine potenzielle kombinatorische Explosion der benötigten Schnittstellen mit modifizierbarer, nicht modifizierbarer Ansicht, Nur-Löschen, Nur-Hinzufügen, fester Länge, unveränderlich (für Threading) usw. für jeden möglichen Satz implementierter Optionsmethoden.
quelle
const
Schlüsselwort wie C ++ hätte.vector<int>, const vector<int>, vector<const int>, const vector<const int>
. So weit so gut, aber dann versuchen Sie, Diagramme zu implementieren, und Sie möchten die Diagrammstruktur konstant machen, aber die Knotenattribute modifizierbar usw.can
Methode entwickelt haben, um zu testen, ob eine Operation möglich ist? Es würde die Schnittstelle einfach und schnell halten.Es hört sich für mich so an, als wäre das
Interface Segregation Principle
damals nicht so gut erforscht wie heute. Diese Vorgehensweise (dh Ihre Schnittstelle enthält alle möglichen Operationen und Sie haben "entartete" Methoden, die Ausnahmen für diejenigen auslösen, die Sie nicht benötigen) war beliebt, bevor SOLID und ISP zum De-facto-Standard für Qualitätscode wurden.quelle
Count
Sammlung, ohne sich Gedanken darüber zu machen, welche Arten von Elementen enthalten sind). In Frameworks wie Java, die auf Typ-Erasure basieren, ist dies jedoch kein Problem.Während einige Leute "optionale Methoden" hassen, bieten sie in vielen Fällen eine bessere Semantik als stark getrennte Schnittstellen. Unter anderem berücksichtigen sie die Möglichkeiten, dass ein Objekt im Laufe seiner Lebensdauer Fähigkeiten oder Eigenschaften erhält oder dass ein Objekt (insbesondere ein Wrapper-Objekt) möglicherweise nicht weiß, wann es konstruiert wurde, über welche genauen Fähigkeiten es berichten soll.
Während ich die Java-Collection-Klassen kaum als Paragone für gutes Design bezeichnen werde, würde ich vorschlagen, dass ein gutes Collections-Framework eine große Anzahl optionaler Methoden sowie Möglichkeiten enthält, eine Collection nach ihren Eigenschaften und Fähigkeiten zu befragen . Mit einem solchen Entwurf kann eine einzelne Wrapper-Klasse mit einer Vielzahl von Sammlungen verwendet werden, ohne dass versehentlich die Fähigkeiten der zugrunde liegenden Sammlung beeinträchtigt werden. Wenn Methoden nicht optional wären, müsste für jede Kombination von Features, die Auflistungen möglicherweise unterstützen, eine andere Wrapper-Klasse verwendet werden. In bestimmten Situationen könnten einige Wrapper nicht verwendet werden.
Wenn eine Auflistung beispielsweise das Schreiben eines Elements nach Index oder das Anhängen von Elementen am Ende unterstützt, das Einfügen von Elementen in der Mitte jedoch nicht unterstützt, ist für den Code, der es in einen Wrapper einschließen möchte, der alle auf ihm ausgeführten Aktionen protokolliert, eine Version erforderlich des Protokollierungs-Wrappers, der die genaue Kombination der unterstützten Fähigkeiten bereitstellte, oder wenn keiner verfügbar war, müsste ein Wrapper verwendet werden, der entweder Anhängen oder Schreiben nach Index, aber nicht beides unterstützt. Wenn jedoch eine vereinheitlichte Erfassungsschnittstelle alle drei Methoden als "optional" bereitstellte, dann aber Methoden enthielt, um anzugeben, welche der optionalen Methoden verwendet werden könnten, könnte eine einzelne Wrapper-Klasse Auflistungen verarbeiten, die eine beliebige Kombination von Funktionen implementieren. Auf die Frage, welche Funktionen unterstützt werden, könnte ein Wrapper einfach melden, was auch immer die gekapselte Sammlung unterstützt.
Es ist zu beachten, dass das Vorhandensein von "optionalen Fähigkeiten" in einigen Fällen ermöglichen kann, dass aggregierte Sammlungen bestimmte Funktionen auf eine Weise implementieren, die viel effizienter ist, als dies möglich wäre, wenn Fähigkeiten durch das Vorhandensein von Implementierungen definiert würden. Angenommen, eine
concatenate
Methode wurde verwendet, um eine zusammengesetzte Auflistung aus zwei anderen zu bilden, wobei die erste zufällig eine ArrayList mit 1.000.000 Elementen und die letzte eine Auflistung mit zwanzig Elementen war, die nur von Anfang an iteriert werden konnte. Wenn die zusammengesetzte Auflistung nach dem 1.000.013sten Element (Index 1.000.012) gefragt würde, könnte sie die ArrayList nach der Anzahl der enthaltenen Elemente (dh 1.000.000) fragen, diese vom angeforderten Index subtrahieren (Ergebnis 12), zwölf Elemente auslesen und überspringen Auflistung, und geben Sie dann das nächste Element zurück.In einer solchen Situation wäre es, obwohl die zusammengesetzte Sammlung keine unmittelbare Möglichkeit hätte, ein Element nach Index zurückzugeben, viel schneller, die zusammengesetzte Sammlung nach dem 1.000.013sten Element zu fragen, als 1.000.013 Elemente einzeln auszulesen und alle außer den letzten zu ignorieren einer.
quelle
AsXXX
die Basisschnittstelle eine Methode enthalten, die das Objekt zurückgibt, für das sie aufgerufen wird, wenn sie diese Schnittstelle implementiert, oder ein Wrapper-Objekt zurückgibt, das diese Schnittstelle unterstützt, sofern dies möglich ist eine Ausnahme, wenn nicht. Zum BeispielImmutableCollection
könnte eine Schnittstelle vertraglich vorgeschrieben sein ...Ich würde es den ursprünglichen Entwicklern zuschreiben, die es damals einfach nicht besser wussten. Seit 1998, als Java 2 und Collections zum ersten Mal veröffentlicht wurden, haben wir im OO-Design einen langen Weg zurückgelegt. Was jetzt wie offensichtlich schlechtes Design aussieht, war in den frühen Tagen von OOP nicht so offensichtlich.
Möglicherweise wurde dies jedoch getan, um zusätzliches Wirken zu verhindern. Wenn es eine zweite Schnittstelle wäre, müssten Sie Ihre Kollektionsinstanzen umwandeln, um diese optionalen Methoden aufzurufen, was ebenfalls hässlich ist. So wie es jetzt ist, würden Sie sofort eine UnsupportedOperationException abfangen und Ihren Code korrigieren. Aber wenn es zwei Schnittstellen gäbe, müssten Sie instanceof verwenden und überall gießen. Vielleicht hielten sie das für einen gültigen Kompromiss. Auch in den frühen Java-2-Tagen wurde instanceof wegen seiner langsamen Leistung stark verpönt. Möglicherweise haben sie versucht, eine übermäßige Nutzung zu verhindern.
Natürlich handelt es sich um wilde Spekulationen, ich bezweifle, dass wir dies jemals mit Sicherheit beantworten könnten, wenn nicht einer der Architekten der Originalsammlung zustimmt.
quelle
Collection
und nicht a zurückgibtMutableCollection
, ist dies ein klares Zeichen dafür, dass sie nicht geändert werden soll. Ich weiß nicht, warum irgendjemand sie besetzen müsste. Wenn Sie separate Schnittstellen haben, werden diese Fehler zur Kompilierungszeit angezeigt, anstatt zur Laufzeit eine Ausnahme zu erhalten. Je früher Sie den Fehler erhalten, desto besser.const
Objekt zurückgeben und dem Benutzer sofort mitteilen, dass das Objekt nicht geändert werden kann.