Ich habe zwei Objekte, die eine 'Bar / Club' darstellen (ein Ort, an dem Sie trinken / Kontakte knüpfen).
In einem Szenario benötige ich den Balkennamen, die Adresse, die Entfernung und den Slogon
In einem anderen Szenario benötige ich den Balkennamen, die Adresse, die Website-URL und das Logo
Ich habe also zwei Objekte, die dasselbe darstellen, aber unterschiedliche Felder haben.
Ich verwende gerne unveränderliche Objekte, daher werden alle Felder vom Konstruktor festgelegt .
Eine Möglichkeit besteht darin, zwei Konstruktoren zu haben und die anderen Felder auf Null zu setzen, dh:
class Bar {
private final String name;
private final Distance distance;
private final Url url;
public Bar(String name, Distance distance){
this.name = name;
this.distance = distance;
this.url = null;
}
public Bar(String name, Url url){
this.name = name;
this.distance = null;
this.url = url;
}
// getters
}
Ich mag das nicht, da Sie null überprüfen müssten, wenn Sie die Getter verwenden
In meinem realen Beispiel hat das erste Szenario 3 Felder und das zweite Szenario ungefähr 10, also wäre es ein echtes Problem, zwei Konstruktoren zu haben , die Anzahl der Felder, die ich als null deklarieren müsste, und wenn das Objekt verwendet wird, würden Sie es nicht tun. Ich weiß nicht, welche Bar
Sie wo verwenden und welche Felder null wären und welche nicht.
Welche anderen Optionen habe ich?
Zwei Klassen genannt BarPreview
und Bar
?
Irgendeine Art von Vererbung / Schnittstelle?
Noch etwas, das großartig ist?
Bar
als Kennung gefunden!You should only ask practical, answerable questions based on actual problems that you face.
Und genau das passiert hierAntworten:
Meine Gedanken:
Eine "Leiste", wie sie in Ihrer Domain dargestellt ist, enthält alle Dinge, die an beiden Orten benötigt werden: Name, Adresse, URL, Logo, Slogan und "Entfernung" (ich vermute vom Standort des Anforderers). Daher sollte es in Ihrer Domain eine "Balken" -Klasse geben, die die maßgebliche Datenquelle für einen Balken darstellt, unabhängig davon, wo die Daten später verwendet werden. Diese Klasse sollte veränderbar sein, damit Änderungen an den Daten der Leiste vorgenommen und bei Bedarf gespeichert werden können.
Sie haben jedoch zwei Stellen, an denen die Daten dieses Balkenobjekts benötigt werden, und beide benötigen nur eine Teilmenge (und Sie möchten nicht, dass diese Daten geändert werden). Die übliche Antwort ist ein "Datenübertragungsobjekt" oder DTO; ein POJO (einfaches altes Java-Objekt), das die unveränderlichen Eigenschafts-Getter enthält. Diese DTOs können durch Aufrufen einer Methode für das Hauptobjekt der Balkendomäne erstellt werden: "toScenario1DTO ()" und "toScenario2DTO ()"; Das Ergebnis ist ein hydratisiertes DTO (was bedeutet, dass Sie den langen, komplizierten Konstruktor nur an einer Stelle verwenden müssen).
Wenn Sie jemals Daten an die Hauptdomänenklasse zurücksenden mussten (um sie zu aktualisieren; wozu dienen Daten, wenn Sie sie nicht nach Bedarf ändern können, um den aktuellen Status der realen Welt widerzuspiegeln?), Können Sie eine der folgenden Methoden erstellen DTOs oder verwenden Sie ein neues veränderbares DTO und geben Sie es mit der Methode "updateFromDto ()" an die Bar-Klasse zurück.
EDIT: um ein Beispiel zu geben:
Die Adressen-, Entfernungs- und URL-Klassen sollten entweder selbst unveränderlich sein oder bei Verwendung in den DTOs durch unveränderliche Gegenstücke ersetzt werden.
quelle
Wenn Sie sich nur für eine Teilmenge der Eigenschaften interessieren und sicherstellen möchten, dass sie nicht verwechselt werden, erstellen Sie zwei Schnittstellen und verwenden Sie diese, um mit Ihrem Basisobjekt zu sprechen.
quelle
Bar
Klasse gebenDas Builder-Muster (oder etwas in der Nähe davon) könnte hier von Nutzen sein.
Unveränderliche Objekte zu haben ist eine bewundernswerte Sache, aber die Realität ist, dass mit Reflection in Java nichts wirklich sicher ist ;-).
quelle
HawaiianPizzaBuilder
funktioniert, weil die benötigten Werte fest codiert sind. Wie können Sie dieses Muster jedoch verwenden, wenn die Werte abgerufen und an einen Konstruktor übergeben werden? DasHawaiianPizzaBuilder
hätte noch alle Getter, dieSpicyPizzaBuilder
das so null hat ist möglich. Es sei denn, Sie kombinieren dies mit @JarrodsNull Object Pattern
. Ein Codebeispiel mitBar
würde Ihren Standpunkt vermittelnDer entscheidende Punkt hierbei ist der Unterschied zwischen dem, was ein "Balken" ist und wie Sie ihn verwenden in dem einen oder anderen Kontext verwenden.
Die Leiste ist eine einzelne Einheit in der realen Welt (oder eine künstliche Welt wie ein Spiel), und nur EINE Objektinstanz sollte sie darstellen. Wenn Sie diese Instanz später nicht aus einem Codesegment erstellen, sondern aus einer Konfigurationsdatei oder einer Datenbank laden, wird dies deutlicher.
(Um noch esoterischer zu sein: Jede Balkeninstanz hat einen anderen Lebenszyklus als das Objekt, das sie darstellt, wenn Ihr Programm ausgeführt wird. Selbst wenn Sie einen Quellcode haben, der diese Instanz erstellt, bedeutet dies, dass die beschriebene Balkenentität "existiert" "in einem Ruhezustand in Ihrem Quellcode und" erwachen ", wenn dieser Code ihn tatsächlich im Speicher erstellt ...)
Entschuldigung für den langen Start, aber ich hoffe, das macht meinen Standpunkt klar. Sie haben eine Bar - Klasse alle Attribute mit , dass Sie jemals brauchen würde, und eine Bar Instanz jede Bar Einheit darstellt. Dies ist in Ihrem Code korrekt und unabhängig davon, wie Sie dieselbe Instanz in verschiedenen Kontexten anzeigen möchten .
Letzteres kann durch zwei verschiedene Schnittstellen dargestellt werden werden, die die erforderlichen Zugriffsmethoden enthalten (getName (), getURL (), getDistance ()), und die Bar-Klasse sollte beide implementieren. (Und vielleicht ändert sich die "Entfernung" in "Ort" und getDistance () wird zu einer Berechnung von einem anderen Ort :-))
Die Erstellung ist jedoch für die Bar-Entität und nicht für die Art und Weise, wie Sie diese Entität verwenden möchten: ein Konstruktor, alle Felder.
EDITED: Ich kann Code schreiben! :-)
quelle
Angemessenes Muster
Was Sie suchen, wird am häufigsten als das bezeichnet
Null Object Pattern
. Wenn Ihnen der Name nicht gefällt, können Sie ihn alsUndefined Value Pattern
dieselbe Bezeichnung für dieselbe Semantik bezeichnen. Manchmal wird dieses Muster genanntPoison Pill Pattern
.In all diesen Fällen ist das Objekt ein Ersatz oder steht für eine
Default Value
stattnull. It doesn't replace the semantic of
nullbut makes it easier to work with the data model in a more predictable way because
null "sollte jetzt niemals ein gültiger Zustand sein.Es ist ein Muster, bei dem Sie eine spezielle Instanz einer bestimmten Klasse reservieren, um eine andere
null
Option als darzustellenDefault Value
. Auf diese Weise müssen Sie nicht überprüfennull
, sondern können die Identität anhand Ihrer bekanntenNullObject
Instanz überprüfen . Sie können Methoden und ähnliches sicher aufrufen, ohne sich Sorgen machen zu müssenNullPointerExceptions
.Auf diese Weise ersetzen Sie Ihre
null
Aufgaben durch ihre repräsentativenNullObject
Instanzen und Sie sind fertig.Richtige objektorientierte Analyse
Auf diese Weise können Sie einen gemeinsamen
Interface
Polymorphismus haben und trotzdem vor dem Fehlen von Daten in den spezifischen Implementierungen der Schnittstelle geschützt sein. Einige habenBar
möglicherweise keine Webpräsenz, andere verfügen zum Zeitpunkt der Erstellung möglicherweise nicht über Standortdaten.Null Object Patter
Mit dieser Option können Sie für jede diesermarker
Daten einen Standardwert angeben, der für die Daten gilt, die dasselbe aussagen. Hier wurde nichts angegeben, ohne dass die Prüfung durchgeführt werden mussNullPointerException
überall durchgeführt werden muss.Richtiges objektorientiertes Design
Haben Sie zuerst eine
abstract
Implementierung, die eine Supermenge aller Attribute ist, die beideBar
undClub
gemeinsam nutzen.Dann können Sie Unterklassen dieser
Establishment
Klasse implementieren und nur die spezifischen Dinge hinzufügen, die Sie für jede der KlassenBar
und benötigenClub
, die für die andere nicht gelten.Beharrlichkeit
Diese Platzhalterobjekte können bei korrekter Konstruktion auch ohne besondere Behandlung transparent in einer Datenbank gespeichert werden.
Zukunftssicher
Wenn Sie sich dazu entschlossen haben, später auf den Zug Inversion of Control / Dependency Injection zu springen, können Sie mit diesem Muster auch diese Markierungsobjekte einfach injizieren.
quelle
Ich denke, das Problem ist, dass Sie in keinem dieser Szenarien einen Balken modellieren (und Sie modellieren zwei verschiedene Probleme, Objekte usw.). Wenn ich eine Klasse-Bar sehe, würde ich einige Funktionen erwarten, die sich auf Getränke, die Menüs, die verfügbaren Plätze beziehen, und Ihr Objekt hat nichts davon. Wenn ich das Verhalten Ihrer Objekte sehe, modellieren Sie Informationen über eine Einrichtung. Bar ist das, wofür Sie diese in diesem Moment verwenden, aber es ist nicht das eigentliche Verhalten, das sie implementieren. (In einem anderen Kontext: Wenn Sie eine Ehe modellieren, haben Sie zwei Instanzvariablen: Person Ehefrau; Person Ehemann; eine Frau ist die aktuelle Rolle, die Sie diesem Objekt in diesem Moment geben, aber das Objekt ist immer noch eine Person). Ich würde so etwas machen:
quelle
Es ist wirklich nicht nötig, dies zu komplizieren. Sie benötigen zwei verschiedene Arten von Objekten? Bilden Sie zwei Klassen.
Wenn Sie sie gleichermaßen bearbeiten müssen, sollten Sie eine Schnittstelle hinzufügen oder Reflektion verwenden.
quelle
null
. Denken Sie daran ,null
Mittel , um das Fehlen von Daten , nicht das Fehlen des Attributs.Meine Antwort an alle, die Probleme dieser Art haben, besteht darin , sie in überschaubare Schritte zu unterteilen .
BarOne
undBarTwo
(oder rufen Sie beide auf,Bar
jedoch in unterschiedlichen Paketen).bar
beleidigende Klasse sind, wenn nicht umbenannt in das, was sie jetzt darstelltinterface
odersuperclass
mit dem gemeinsamen Verhalten extrahiereninterface
oder haben, könnensuperclass
Sie ein oder erstellenbuilder
oderfactory
Ihre Implementierungsobjekte erstellen / abrufen(4 und 5 sind die anderen Antworten auf diese Frage)
quelle
Sie benötigen eine Basisklasse, z. B. Location, die einen Namen und eine Adresse hat . Nun haben Sie zwei Klassen Bar und BarPreview die Basisklasse erweitern Lage . In jeder Klasse initialisieren Sie die allgemeinen Variablen der Superklasse und dann Ihre eindeutigen Variablen:
Und ähnlich für die BarPreview-Klasse.
Wenn Sie nachts besser schlafen können, ersetzen Sie alle Instanzen von Location in meinem Code durch AnythingYouThinkWouldBeAnAppropriateNameForAThingThatABarExtendsSuchAsFoodServicePlace - ffs.
quelle
final
.final
die Felder @David.Bar
sollte aber nicht,extend Location
dass das keinen Sinn macht. VielleichtBar extends BaseBar
undBarPreview extends BaseBar
diese Namen klingen auch nicht wirklich gut, ich hoffte auf etwas eleganteres