Ist es für Klassen mit optionalen Feldern besser, Vererbung oder eine nullfähige Eigenschaft zu verwenden? Betrachten Sie dieses Beispiel:
class Book {
private String name;
}
class BookWithColor extends Book {
private String color;
}
oder
class Book {
private String name;
private String color;
//when this is null then it is "Book" otherwise "BookWithColor"
}
oder
class Book {
private String name;
private Optional<String> color;
//when isPresent() is false it is "Book" otherwise "BookWithColor"
}
Der Code, der von diesen drei Optionen abhängt, wäre:
if (book instanceof BookWithColor) { ((BookWithColor)book).getColor(); }
oder
if (book.getColor() != null) { book.getColor(); }
oder
if (book.getColor().isPresent()) { book.getColor(); }
Der erste Ansatz erscheint mir natürlicher, ist aber möglicherweise weniger lesbar, da Casting erforderlich ist. Gibt es einen anderen Weg, um dieses Verhalten zu erreichen?
java
inheritance
class
null
Bojan VukasovicTest
quelle
quelle
Antworten:
Das hängt von den Umständen ab. Das spezifische Beispiel ist unrealistisch, da
BookWithColor
in einem realen Programm keine Unterklasse aufgerufen werden würde .Im Allgemeinen sollte eine Eigenschaft, die nur für bestimmte Unterklassen sinnvoll ist, nur in diesen Unterklassen vorhanden sein.
Zum Beispiel, wenn
Book
hatPhysicalBook
undDigialBook
als Abkömmlinge, dannPhysicalBook
könnte eine hatweight
Eigenschaft undDigitalBook
einesizeInKb
Eigenschaft. WerdeDigitalBook
aber nicht habenweight
und umgekehrt.Book
wird keine Eigenschaft haben, da eine Klasse nur Eigenschaften haben sollte, die von allen Nachkommen geteilt werden.Ein besseres Beispiel sind Klassen aus echter Software. Die
JSlider
Komponente hat das FeldmajorTickSpacing
. Da nur ein Schieberegler "Häkchen" hat, ist dieses Feld nur für sinnvollJSlider
und seine Nachkommen . Es wäre sehr verwirrend, wenn andere Geschwisterkomponenten wieJButton
einmajorTickSpacing
Feld hätten.quelle
Ein wichtiger Punkt, der anscheinend nicht erwähnt wurde: In den meisten Sprachen können Instanzen einer Klasse nicht ändern, von welcher Klasse sie stammen. Wenn Sie also ein Buch ohne Farbe hatten und eine Farbe hinzufügen möchten, müssen Sie ein neues Objekt erstellen, wenn Sie verschiedene Klassen verwenden. Und dann müssten Sie wahrscheinlich alle Verweise auf das alte Objekt durch Verweise auf das neue Objekt ersetzen.
Wenn "Bücher ohne Farbe" und "Bücher mit Farbe" Instanzen derselben Klasse sind, ist das Hinzufügen der Farbe oder das Entfernen der Farbe weniger problematisch. (Wenn Ihre Benutzeroberfläche eine Liste mit "Büchern mit Farbe" und "Büchern ohne Farbe" anzeigt, müsste sich diese Benutzeroberfläche offensichtlich ändern, aber ich würde davon ausgehen, dass Sie dies sowieso tun müssen, ähnlich einer "Liste mit roten Büchern" books "und" list of green books ").
quelle
Stellen Sie sich eine JavaBean (wie Ihre
Book
) als Datensatz in einer Datenbank vor. Optionale Spalten sind,null
wenn sie keinen Wert haben, aber es ist völlig legal. Daher Ihre zweite Option:Ist das vernünftigste. 1
Seien Sie jedoch vorsichtig, wie Sie die
Optional
Klasse verwenden. Zum Beispiel ist dies nichtSerializable
der Fall, was normalerweise ein Merkmal einer JavaBean ist. Hier einige Tipps von Stephen Colebourne :Daher innerhalb Ihrer Klasse sollten Sie verwenden ,
null
um darzustellen , dass das Feld nicht vorhanden ist, aber wenn diecolor
Blätter desBook
(als Rückgabetyp) sollte es mit einem eingewickelt werden ,Optional
.Dies sorgt für klares Design und besser lesbaren Code.
1 Wenn Sie beabsichtigen , für eine
BookWithColor
eine ganze „Klasse“ der Bücher zu verkapseln , die haben Fähigkeiten spezialisiert auf andere Bücher, dann wäre es sinnvoll zu nutzen Erbschaft machen.quelle
Optional
Klasse zu Java genau zu diesem Zweck eine Rolle gespielt haben . Es ist vielleicht nicht OO, aber es ist sicherlich eine gültige Meinung.Sie sollten entweder eine Schnittstelle (keine Vererbung) oder eine Art Eigenschaftsmechanik (möglicherweise sogar etwas, das wie das Ressourcenbeschreibungsframework funktioniert) verwenden.
Also entweder
oder
Klarstellung (bearbeiten):
Fügen Sie einfach optionale Felder in die Entitätsklasse ein
Vor allem mit dem Optional-Wrapper(edit: siehe 4castle -Antwort) halte ich dies (Hinzufügen von Feldern in der ursprünglichen Entität) für eine praktikable Möglichkeit, neue Eigenschaften in kleinem Maßstab hinzuzufügen. Das größte Problem bei diesem Ansatz ist, dass er möglicherweise gegen eine geringe Kopplung wirkt.Stellen Sie sich vor, Ihre Buchklasse ist in einem speziellen Projekt für Ihr Domain-Modell definiert. Nun fügen Sie ein weiteres Projekt hinzu, das das Domänenmodell für eine spezielle Aufgabe verwendet. Diese Aufgabe erfordert eine zusätzliche Eigenschaft in der Buchklasse. Entweder haben Sie die Vererbung (siehe unten) oder Sie müssen das allgemeine Domänenmodell ändern, um die neue Aufgabe zu ermöglichen. In letzterem Fall kann es vorkommen, dass Sie eine Reihe von Projekten haben, die alle von ihren eigenen Eigenschaften abhängen, die der Buchklasse hinzugefügt wurden, während die Buchklasse selbst in gewisser Weise von diesen Projekten abhängt, da Sie die Buchklasse ohne die nicht verstehen können zusätzliche Projekte.
Warum ist die Vererbung problematisch, wenn es darum geht, zusätzliche Eigenschaften bereitzustellen?
Wenn ich Ihre Beispielklasse "Buch" sehe, denke ich an ein Domänenobjekt, das häufig viele optionale Felder und Untertypen enthält. Stellen Sie sich vor, Sie möchten eine Eigenschaft für Bücher hinzufügen, die eine CD enthalten. Es gibt jetzt vier Arten von Büchern: Bücher, Bücher mit Farbe, Bücher mit CD, Bücher mit Farbe und CD. Sie können diese Situation nicht mit Vererbung in Java beschreiben.
Mit Schnittstellen umgehen Sie dieses Problem. Sie können die Eigenschaften einer bestimmten Buchklasse einfach über Schnittstellen zusammenstellen. Durch Delegation und Komposition wird es einfach, genau die Klasse zu erhalten, die Sie möchten. Bei der Vererbung erhalten Sie normalerweise einige optionale Eigenschaften in der Klasse, die nur vorhanden sind, weil eine Geschwisterklasse sie benötigt.
Lesen Sie weiter, warum Vererbung oft eine problematische Idee ist:
Warum wird Vererbung von OOP-Befürwortern im Allgemeinen als eine schlechte Sache angesehen?
JavaWorld: Warum sich ausdehnt, ist böse
Das Problem beim Implementieren einer Reihe von Schnittstellen zum Erstellen einer Reihe von Eigenschaften
Wenn Sie Schnittstellen zur Erweiterung verwenden, ist alles in Ordnung, solange Sie nur einen kleinen Satz davon haben. Insbesondere wenn Ihr Objektmodell von anderen Entwicklern verwendet und erweitert wird, z. B. in Ihrem Unternehmen, wird die Anzahl der Schnittstellen zunehmen. Und schließlich erstellen Sie eine neue offizielle "Eigenschaftsschnittstelle", die eine Methode hinzufügt, die Ihre Kollegen bereits in ihrem Projekt für Kunde X für einen völlig unabhängigen Anwendungsfall verwendet haben - Ugh.
edit: Ein weiterer wichtiger Aspekt wird von gnasher729 erwähnt . Oft möchten Sie einem vorhandenen Objekt optionale Eigenschaften dynamisch hinzufügen. Mit Vererbung oder Schnittstellen müssten Sie das gesamte Objekt mit einer anderen Klasse neu erstellen, die alles andere als optional ist.
Wenn Sie Erweiterungen Ihres Objektmodells in einem solchen Ausmaß erwarten, sind Sie besser dran, wenn Sie explizit die Möglichkeit einer dynamischen Erweiterung modellieren . Ich schlage so etwas wie oben vor, wo jede "Erweiterung" (in diesem Fall Eigenschaft) eine eigene Klasse hat. Die Klasse fungiert als Namespace und Bezeichner für die Erweiterung. Wenn Sie die Paketnamenskonventionen entsprechend festlegen, werden auf diese Weise unendlich viele Erweiterungen ermöglicht, ohne dass der Namespace für Methoden in der ursprünglichen Entitätsklasse verschmutzt wird.
In der Spieleentwicklung stoßen Sie häufig auf Situationen, in denen Sie Verhalten und Daten in vielen Variationen zusammenstellen möchten. Aus diesem Grund wurde das Architekturmuster Entity-Component-System in Spielentwicklungskreisen sehr beliebt. Dies ist auch ein interessanter Ansatz, den Sie sich ansehen sollten, wenn Sie viele Erweiterungen für Ihr Objektmodell erwarten.
quelle
PropertiesHolder
wäre das wahrscheinlich eine abstrakte Klasse. Aber wofür würden Sie eine Implementierung vorschlagengetProperty
odergetPropertyValue
? Die Daten müssen noch irgendwo in einem Instanzfeld gespeichert werden. Oder würden Sie a verwendenCollection<Property>
, um die Eigenschaften zu speichern, anstatt Felder zu verwenden?Book
Verlängerung vorgenommenPropertiesHolder
. DasPropertiesHolder
sollte normalerweise ein Mitglied in allen Klassen sein, die diese Funktionalität benötigen. Die Implementierung würde einMap<Class<? extends Property<?>>,Property<?>>
oder so ähnlich halten. Dies ist der hässliche Teil der Implementierung, bietet aber ein wirklich nettes Framework, das sogar mit JAXB und JPA funktioniert.Wenn
Book
Klasse ohne Farbeigenschaft wie unten abgeleitet werden kanndann ist Vererbung sinnvoll.
quelle
color
es sich um eine private Klasse handelt, sollte sich keine abgeleitete Klasse darum kümmern müssencolor
. Fügen Sie einfach einige Konstruktoren hinzu,Book
die ignoriertcolor
werdennull
.