Jede gute Beispiele für Erben von einer konkreten Klasse?
Hintergrund:
Als Java-Programmierer, der ich ausgiebig Erben (statt: implementieren) von Schnittstellen, und manchmal habe ich das design abstrakte Basis-Klassen. Allerdings habe ich nie wirklich das Bedürfnis verspürt, Unterklasse eine konkrete (nicht-abstrakte) Klasse (in den Fällen wo ich es gemacht habe, es stellte sich später heraus, dass eine andere Lösung, wie delegation wäre besser gewesen).
So, ich bin jetzt Anfang zu spüren, dass es fast keine situation, wo Erben von einer konkreten Klasse angemessen ist. Für eine Sache, die Liskov-substitution-Prinzip (LSP) scheint fast unmöglich zu erfüllen, für nicht-triviale Klassen; auch viele anderen Fragen hier scheinen echo eine ähnliche Meinung.
Also meine Frage:
In die situation (wenn überhaupt), macht es tatsächlich Sinn macht, Erben von einer konkreten Klasse?
Können Sie ein konkretes, reales Beispiel für eine Klasse, die erbt von einem anderen konkreten Klasse, wo Sie glauben, dies ist das beste design, das angesichts der Zwänge? Ich B besonders interessiert Beispiele, die Erfüllung der LSP (oder Beispiele, wo die Befriedigung LSP scheint unwichtig).
Ich hauptsächlich ein Java-hintergrund, aber ich bin daran interessiert, Beispiele aus jeder Sprache.
- Bei der Codierung wird eine Hierarchie von Klassen, wie die des trivialen Hund extends Tier. Ich in der Regel verwenden Sie Vererbung, ausgiebig bei der Codierung Datenmodelle. Für mich, Modellierung wirklichen Leben Dinge, ist, wenn inheritence kommt, um zu spielen.
- Vorausgesetzt, ich habe, was du meinst, durch delegation: Ein guter Grund für die Verwendung von Unterklassen ist, dass es ein so großes Kopfzerbrechen Klassen definieren, delegieren alle Methoden einer Schnittstelle zu einer anderen Klasse in Java und vielen anderen OOP-Sprachen auch. So die gängige Praxis zu akkumulieren mehr und mehr interfaces in Klassen durch den Aufbau einer Hierarchie, in der jeder Unterklasse implementiert eine andere Schnittstelle der Basis-Klasse nicht. Im Idealfall, aber es macht Haufen mehr Sinn zu delegieren, Schnittstellen.
- Es gibt den pathologischen Fall der Erben von Object ...
- +1 für eine ausführliche Erklärung...
- Meine Erwähnung der "delegation" bezeichnet die "delegation pattern". Ich bearbeitet meine Frage auf einen link.
- Was macht Sie glauben, meine "Wiederverwendung von code-Statistiken" niedrig sind? Es gibt viele andere Möglichkeiten, um code wiederzuverwenden neben der Vererbung.
- Ich habe gefragt, die gleiche Sache. Überprüfen Sie heraus die Frage, die ich fragte: stackoverflow.com/questions/3351666/why-use-inheritance-at-all
- in diesem Fall meinte ich das gleiche. Meinst du nicht, dass Java hätte es viel einfacher zu delegieren? Wenn die syntax zu delegieren, eine gesamte Schnittstelle zu einer Instanz war zu kurz, ich würde nie die Vererbung verwenden, es sei denn, eingeschränkt durch eine Bibliothek, die ich verwende. Aber, wie es jetzt ist, ist es einfach ein Kopfschmerz zu delegieren, wenn Ihre Objekte zu implementieren, die viele Schnittstellen oder große Schnittstellen.
- Verwandte: Warum sollte eine Klasse sein, die alles andere als "abstract" oder "final/versiegelt"?
- Auch im Zusammenhang: Ist die Vererbung konkreter Klassen, die das böse?
Du musst angemeldet sein, um einen Kommentar abzugeben.
Sie haben oft eine Skelett-Implementierungen für eine Schnittstelle
I
. Wenn Sie bieten Erweiterbarkeit ohne abstrakte Methoden (z.B. über Haken), ist es vorzuziehen, eine nicht-abstrakte Skelett-Klasse, weil Sie instanziieren können es.Ein Beispiel wäre ein Weiterleitung wrapper-Klassen, um in der Lage sein, um vorwärts zu einem anderen Objekt einer konkreten Klasse
C
UmsetzungI
, z.B. die Aktivierung Dekoration oder einfache code-Wiederverwendung vonC
zimmerreserviereung, ohne das Sie Erben vonC
. Sie finden ein solches Beispiel in Effektive Java Element 16, favor composition over inheritance. (Ich will nicht es hier zu posten wegen der Urheberrechte, aber es ist wirklich einfach die Weiterleitung an alle MethodenaufrufeI
auf den umschlossenen Umsetzung).MouseAdapter
etc), die NOOP-Implementierungen von vielen-Methode-Ereignis-listener-interfaces, GUI-Komponenten wäre noch schmerzhafter zu schreiben.abstract
, also in diesem Beispiel nicht wirklich meine Frage.foo
erklären würde, sowohl eine abstrakte Klassefoo
und eine versiegelte Klasse__impl_foo
was geerbtfoo
aber enthielt nichts anderes. Nehmen wir weiter an, dassnew foo()
bekommen würde umgewandeltnew __impl_foo()
. Würden solche Substitutionen zu ändern, alle andere Semantik als die Reflexion Namen von dem konkreten Typ?Ich denke, das folgende ist ein gutes Beispiel, wenn es kann sinnvoll sein:
Weiteres gutes Beispiel ist die Vererbung der exceptions:
Einen sehr häufigen Fall, ich denken kann, ist eine Ableitung von grundlegenden Steuerelemente der Benutzeroberfläche, wie Formen, Textfelder, comboboxes, usw.. Sie sind vollständige, konkrete, und auch in der Lage zu stehen; jedoch die meisten von Ihnen sind auch sehr basic, und manchmal Ihre Standard-Verhalten ist nicht, was Sie wollen. Praktisch niemand würde beispielsweise die Verwendung einer Instanz von einer unverfälschten Form, es sei denn, möglicherweise wurden Sie schafft eine völlig dynamische UI-layer.
Beispielsweise in ein Stück software, die ich schrieb vor kurzem erreichten Reifegrad (was bedeutet, dass ich nicht genügend Zeit, um den Fokus in Erster Linie auf der Entwicklung von it 🙂 ), fand ich, ich brauchte, um hinzuzufügen, "lazy loading" - Funktion für ComboBoxes, so würde es sich nicht nehmen, 50 Jahre (in computer-Jahren) für das erste Fenster zu laden. Ich musste auch die Fähigkeit, automatisch filtern Sie die verfügbaren Optionen in einer ComboBox basierend auf was gezeigt wurde, in einem anderen, und schließlich brauchte ich einen Weg, um "Spiegel" ein Kombinationsfeld-Wert in andere editierbare Steuerung, und nehmen Sie eine änderung in einer Kontrolle passieren die anderen als gut. Also erweiterte ich die basic ComboBox geben Sie diese zusätzlichen Eigenschaften, und erstellt zwei neue Arten: LazyComboBox, und dann weiter, MirroringComboBox. Beide basieren auf der völlig intakt ist, konkrete ComboBox-Steuerelement, nur überschreiben einige Verhaltensweisen und hinzufügen von ein paar andere. Sie sind nicht sehr lose gekoppelt und damit nicht so SOLIDE, aber die zusätzliche Funktionalität ist generisch genug, dass, wenn ich musste, ich konnte umschreiben, dass diese beiden Klassen von Grund auf neu zu tun, den gleichen job, vielleicht besser.
Generell, das einzige mal, dass ich eine Ableitung von konkreten Klassen ist, wenn Sie in den Rahmen. Die sich aus
Applet
oderJApplet
als triviales Beispiel.JPanel
oderJComponent
wenn Sie gerne ein paar benutzerdefinierte zeichnen auf eine Swing-Komponente.Dies ist ein Beispiel für eine aktuelle Umsetzung, ich bin Unternehmen.
In OAuth 2-Umgebung, da die Dokumentation ist noch im Entwurfsstadium, die Spezifikation hält zu ändern (wie von Zeit zu schreiben, wir sind in der version 21).
So, ich hatte zu verlängern, meine konkrete
AccessToken
Klasse, um Platz für die verschiedenen access-Token.In früheren Entwurf war, gab es keine
token_type
Feld gesetzt, so dass die eigentliche access token ist wie folgt:Nun, mit Access-Token zurückgibt
token_type
habe ichSo, ich kann beide und der Endbenutzer wird nicht klüger. 🙂
In der Zusammenfassung: Wenn Sie möchten, eine benutzerdefinierte Klasse, die hat die gleiche Funktionalität, die von Ihrer konkreten Klasse ohne änderung der Struktur der konkreten Klasse, ich schlage vor, die Ausweitung der konkreten Klasse.
Wie viele frameworks, ASP.NET macht starken Gebrauch von Vererbung zu teilen, Verhalten zwischen den Klassen. Zum Beispiel, HtmlInputPassword hat diese Vererbungshierarchie:
in die gesehen werden können, Beispiele für konkrete Klassen abgeleitet.
Wenn Sie einen Rahmen - und Sie sind sicher, dass Sie das tun wollen - Sie können auch sich selbst zu finden, die einen schönen großen Vererbungshierarchie.
Anderen Anwendungsfall wäre das das Standardverhalten zu überschreiben:
Können sagen, es ist eine Klasse, die verwendet standard Jaxb parser für das Parsen
Nun sagen, das ich verwenden möchte einige unterschiedliche Bindung (Sagen XMLBean) für die parsing-operation,
Jetzt kann ich mit der neuen Bindung mit Wiederverwendung von code, der super-Klasse.
Den Dekorator-Muster, eine praktische Möglichkeit, das hinzufügen zusätzlicher Verhalten zu einer Klasse, ohne dass es zu allgemein, macht starken Gebrauch von Vererbung konkreter Klassen. Es wurde hier schon genannt, aber unter etwas einen wissenschaftlichen Namen, der "Weiterleitung" wrapper-Klasse".
Viele Antworten, aber ich dachte, ich würde meine eigenen $0.02.
Ich überschreiben konkreten Klassen selten, aber unter bestimmten besonderen Umständen. Mindestens 1 bereits erwähnt, bei der framework-Klassen sind so konzipiert, verlängert werden. 2 zusätzliche, in den Sinn kommen mit einigen Beispielen:
1) Wenn ich im Spiel das Verhalten einer konkreten Klasse. Manchmal möchte ich ändern, wie die konkrete Klasse funktioniert oder ich möchte wissen, Wann eine bestimmte Methode aufgerufen wird, so kann ich etwas auslösen. Oft konkrete Klassen definieren Sie eine hook-Methode, deren einzige Verwendung ist für die Unterklassen diese Methode überschreiben.
Beispiel: Wir überschrieb
MBeanExporter
denn wir müssen in der Lage sein, um die Registrierung einer JMX-bean:Unserer Klasse:
Hier ist ein weiteres gutes Beispiel.
LinkedHashMap
ist entworfen, um IhreremoveEldestEntry
Methode überschrieben.2) Wenn eine Klasse teilt sich eine erhebliche überlappung mit der konkreten Klasse, außer für einige Verbesserungen in der Funktionalität.
Beispiel: Mein ORMLite Projekt behandelt anhaltenden
Long
Objekt-Felder undlong
primitive Felder. Beide haben fast die identische definition.LongObjectType
bietet alle Methoden, die beschreiben, wie die Datenbank beschäftigt sich mitlong
Felder.während
LongType
überschreibtLongObjectType
und nur Verbesserungen eine einzelne Methode zu sagen, mit der "primitiven".Hoffe, das hilft.
BaseDataType
dass ich es nicht beschreiben, setztisPrimitive()
falsch zu sein. DieLongObjectType
Unterklasse beschreibt dann, wie Sie bestehen Long-undLongType
überschreibt dieisPrimitive()
true zurück. Mit einemBaseLongObjectType
scheint nur fremde für mich.Erbenden konkreten Klassen ist nur option, wenn Sie möchten, verlängern Seite-library-Funktionen.
Beispielsweise der real-life-Nutzung kannst du schauen Hierarchie von DataInputStream, implementiert DataInput-Schnittstelle für FilterInputStream.
Dies ist ein "fast". Schreiben Sie eine applet ohne Verlängerung
Applet
oderJApplet
.Hier ist ein z.B. von der applet-info. Seite.
Weiteres gutes Beispiel wäre die Daten-storage-Typen. Geben, um ein präzises Beispiel: ein rot-schwarz-Baum ist ein spezifischer binärer Baum, aber das abrufen von Daten und anderen Informationen wie Größe behandelt werden können, identisch. Natürlich, eine gute Bibliothek haben sollte, die bereits umgesetzt werden, aber manchmal müssen Sie bestimmte Daten-Typen für Ihr problem.
Ich bin derzeit an der Entwicklung einer Anwendung, die berechnet Matrizen für die Nutzer. Der Benutzer kann die Einstellungen beeinflussen die Berechnung. Es gibt mehrere Typen von Matrizen, die berechnet werden kann, aber es ist eine deutliche ähnlichkeit, vor allem in der Konfigurierbarkeit: die matrix A können alle Einstellungen der matrix B, sondern verfügt über zusätzliche Parameter, die verwendet werden können. In diesem Fall erbte ich von der ConfigObjectB für meine ConfigObjectA und es funktioniert ziemlich gut.
Im Allgemeinen, ist es besser, Erben von einer abstrakten Klasse als von einer konkreten Klasse. Eine konkrete Klasse muss eine definition für seine Darstellung der Daten, und einige Unterklassen müssen eine andere Darstellung. Da eine abstrakte Klasse nicht haben, um eine Darstellung der Daten, Zukunft Unterklassen verwenden können, jede Darstellung ohne Angst vor dem Konflikt mit der, der Sie geerbt hat.
Noch nie fand ich eine situation, wo ich fühlte, Beton inheritence notwendig ist. Aber es könnte einige Situationen für konkrete inheritence, besonders, wenn Sie die Abwärtskompatibilität zu Ihrer software. In diesem Fall u möglicherweise haben sich darauf spezialisiert, eine
class A
aber Sie wollen es, konkret zu sein als Ihre ältere Anwendung möglicherweise verwenden.Ihre Anliegen sind auch hallte in dem klassischen Prinzip "favor composition over inheritance", für die Gründe, die Sie angegeben. Ich kann mich nicht erinnern, das Letzte mal habe ich geerbt von einer konkreten Klasse. Jede common code wiederverwendet werden, die von Kind-Klassen fast immer braucht, um zu erklären, abstrakten Schnittstellen für diese Klassen. In dieser Reihenfolge versuche ich lieber die folgenden Strategien:
Erben von einer konkreten Klasse ist wirklich nicht immer eine gute Idee.
[EDIT] ich werde qualifizieren diese Aussage, indem Sie sagen: ich sehe nicht, eine gute Verwendung für es, wenn Sie haben die Kontrolle über die Architektur. Natürlich, wenn über eine API, die es erwartet, was ist denn Los gonna do? Aber ich verstehe nicht, die design-Entscheidungen von jenen APIs. Die aufrufende Klasse sollten immer in der Lage sein, zu erklären und zu benutzen, eine Abstraktion, die nach der Dependency-Inversion-Prinzip. Wenn eine Kind-Klasse hat zusätzliche Schnittstellen konsumiert werden, würden Sie entweder gegen DIP oder einige hässliche Gießen, um auf diese Schnittstellen.
aus der gdata-Projekt:
com.google.gdata.client.Service ist konzipiert als Basis-Klasse, die angepasst werden können für bestimmte Arten von GData services.
Service javadoc:
Den Service Klasse stellt eine client-Verbindung zu einem GData-service. Es kapselt alle Protokoll-level-Interaktionen mit dem GData server und fungiert als helper-Klasse für höhere level-Einheiten (feeds, Einträge, etc), die invoke-Vorgänge auf dem server und verarbeiten Ihre Ergebnisse.
Diese Klasse stellt die Basis-Ebene gemeinsame Funktionalität für den Zugriff auf GData-service. Es ist auch entworfen, um als Basis-Klasse, die angepasst werden können für bestimmte Arten von GData services. Beispiele von unterstützten Anpassungen gehören:
Authentifizierung - Implementierung einer benutzerdefinierten Authentifizierungs-Mechanismus für services, die eine Authentifizierung erfordern und verwenden Sie etwas anderes als eine HTTP-basic-oder-digest-Authentifizierung.
Erweiterungen - definieren Sie die zu erwartenden Erweiterungen für die Futtermittel -, Einreise-und andere Arten ein, verbunden mit einem service.
Formate - definieren weitere benutzerdefinierte Ressourcen-Repräsentationen, die möglicherweise verbraucht oder produziert von service-und client-Seite Parser und Generatoren, um Sie zu behandeln.
Finde ich die java collection Klassen als ein sehr gutes Beispiel.
Sie haben also ein AbstractCollection mit childs wie AbstractList, AbstractSet, AbstractQueue...
Ich denke, dass diese Hierarchie wurde auch entwickelt.. und nur um sicherzustellen, dass es keine explosion gibt es die Collections-Klasse mit allen inneren statischen Klassen.
Tun Sie das zum Beispiel in der GUI-Bibliotheken. Es macht nicht viel Sinn, Erben von einer reinen Komponente und delegieren Sie an eine Tafel. Es ist wahrscheinlich viel easyer zu Erben von der Platte direkt.
Nur ein allgemeiner Gedanke. Abstrakte Klassen sind etwas fehlt. Es macht Sinn, wenn das, was fehlt, ist in jeder abgeleiteten Klasse. Aber haben Sie vielleicht einen Fall, wo Sie nicht wollen, zu ändern, eine Klasse ist, sondern nur etwas hinzufügen möchten. Zur Vermeidung von Doppelarbeit der code, den Sie Erben würde. Und wenn Sie beide Klassen es wäre Vererbung von einem konkreten Klasse.
Also meine Antwort wäre: In allen Fällen, in denen Sie wirklich nur etwas hinzufügen möchten. Vielleicht kommt das nicht sehr oft.