Ich arbeite in einem System, das auf zwei Arten eine "Versandschätzung" darstellen kann:
- Ein bestimmtes Datum: Der Artikel wird garantiert zu diesem Datum versendet
- Ein Tagesintervall: Der Artikel wird ab heute "X bis Y" Tage versendet
Die Informationen zum Modell sind semantisch identisch, es handelt sich um "die Versandschätzung". Wenn ich die Informationen über den Versandvoranschlag vom System erhalte, kann ich feststellen, ob der Kostenvoranschlag vom ersten oder vom zweiten Formular ist.
Das aktuelle Modell hierfür ähnelt dem folgenden:
class EstimattedShippingDateDetails
{
DateTime? EstimattedShippingDate {get; set;}
Range? EstimattedShippingDayRange {get; set;}
}
Range
ist eine einfache Klasse, um einen "Anfang -> Ende" von ganzen Zahlen zu umbrechen, ähnlich wie folgt:
struct Range
{
int Start {get; set}
int End {get; set}
public override ToString()
{
return String.Format("{0} - {1}", Start, End);
}
}
Dieser Ansatz gefällt mir nicht, da nur eine der Eigenschaften des Schätzmodells jemals ausgefüllt wird und ich bei einer von ihnen auf Null testen und davon ausgehen muss, dass die andere über die Daten verfügt.
Jede der Eigenschaften wird für den Benutzer anders angezeigt, jedoch an derselben Stelle auf der Benutzeroberfläche. Dabei wird eine benutzerdefinierte MVC DisplayTemplate verwendet, in der sich die aktuelle Schaltlogik befindet:
@Model EstimattedShippingDateDetails
@if (Model.EstimattedShippingDate.HasValue)
{
Html.DisplayFor(m => Model.EstimattedShippingDate)
}
else
{
Html.DisplayFor(m => Model.EstimattedShippingDayRange)
}
Wie könnte ich dies modellieren, um es repräsentativer für die tatsächliche Anforderung zu machen und gleichzeitig die Anzeigelogik in einer MVC-Anwendung einfach zu halten?
Ich habe darüber nachgedacht, eine Schnittstelle und zwei Implementierungen zu verwenden, eine für jeden "Schätztyp", aber ich kann mich nicht um eine gemeinsame Schnittstelle für beide kümmern. Wenn ich eine Schnittstelle ohne Mitglieder erstelle, kann ich nicht einheitlich auf die Daten zugreifen, und es ist meiner Meinung nach ein schlechtes Design. Ich wollte auch das Ansichtsmodell so einfach wie möglich halten. Mit diesem Ansatz würde ich jedoch einen "durch Konstruktion korrekten" Code erhalten, da es keine Nullables mehr geben müsste: Jede Implementierung hätte eine nicht nullable Eigenschaft, entweder a DateTime
oder a Range
.
Ich habe auch in Betracht gezogen, nur eine einzige zu verwenden, Range
und wenn Situation Nr. 1 eintritt, verwenden Sie einfach dieselbe DateTime
für beide Start
und End
, aber dies würde zu Komplikationen bei der Ausgabe der Werte an die Benutzeroberfläche führen, da ich dann feststellen müsste, ob es sich um eine statische Aufladung oder ein Intervall von handelt Vergleichen Sie die Werte und formatieren Sie den Bereich richtig, um ihn entweder als einzelnes Datum oder als formatiertes Intervall anzuzeigen.
Es scheint, dass ich ein Konzept brauche, das den Gewerkschaften von Typescript ähnelt: im Grunde eine Eigenschaft, die zwei Arten haben kann. Natürlich gibt es so etwas in C # nicht nativ (nur dynamic
in der Nähe davon).
quelle
Antworten:
Verwenden Sie für alle Versandschätzungen einen Datumsbereich (dh zwei Daten).
Machen Sie für ein einzelnes Datum X und Y gleich.
quelle
DateTime
Objekt, das das erwartete Versanddatum darstellt, oder zwei ganzzahlige Werte mit den minimalen und maximalen Tagen ab heute, an denen der Artikel versendet wird. Wenn ich Datumsangaben standardisiere, muss ich diese Ganzzahlen in ordnungsgemäß erstellte DateTimes konvertieren, was die Komplexität erheblich erhöhen würde, da alles berücksichtigt werden muss, wie Client- / Server-Datumsangaben, UTC und Sommerzeit Unterschiede usw. Ich möchte an dieser Stelle vermeiden, diese Art von Komplexität zu erzeugen.Sie können dies mithilfe der Kapselung modellieren.
Lassen Sie die Klasse 2 Konstruktoren haben, einen für das einzelne Datum und einen für den Datumsbereich.
Bestimmen Sie in der ToString () -Methode den 'Status' der Klasse und generieren Sie die entsprechende formatierte Zeichenfolge.
Fügen Sie andere Methoden hinzu, die Ihren anderen Anforderungen entsprechen.
quelle
Range
Typ an anderen Stellen im System verwendet und in diesem Fall auf die gleiche Weise angezeigt werden kann. Aus diesem Grund muss ich dieRange
Anzeige in einer DisplayTemplate zentralisieren, die wiederverwendet werden kann. Wenn ich dieToString
Methode verwende, um das zu kapseln, verliere ich viel Flexibilität, die Ansichten bieten.state
.ToString()
sollte nur den Zustand "melden". Der Status wird in Konstruktoren, Eigenschaftssetzern usw. bestimmt. Und ich sehe einfach nichts im OP, was darauf hindeutet, dass es einen "zusammenfassenden Zustand" gibt oder geben sollteDies ist ein ziemlich häufiges Problem. Nullables lösen beispielsweise das alltägliche Problem von endDates für Dinge, die noch nicht beendet sind.
Aber ich denke, Sie haben ein schlechtes Beispiel gewählt.
Mit Ihrem genauen Fall von zwei Daten scheint ein Datumsbereich, der morgens beginnt und abends endet, die perfekte Lösung zu sein. Oder vielleicht ein Startdatum und eine ganzzahlige Anzahl zusätzlicher Tage?
Betrachten wir jedoch einen schwierigeren Fall. Ich habe zwei Arten der Lieferung, Post und Abholung vom Geschäft. Diese sind viel unterschiedlicher, der Shop benötigt den Namen und die Adresse, die Post hat Kosten, möglicherweise eine Reihe von Lieferoptionen, Tracking-Codes usw.
Der Standardansatz besteht darin, nach den gemeinsamen Dingen zu suchen, die diese beiden "Lieferoptionen" ausmachen, und diese in eine Basisklasse einzuteilen. Die Unterklasse der beiden spezifischen Fälle mit den zusätzlichen Details, die sie haben / benötigen.
In fast allen Fällen haben Sie mindestens eine ID, einen Typ und eine Beschreibung, die beiden Typen gemeinsam sind. Damit:
quelle
DataTime.Date
Unterkunft und sorgen Sie sich überhaupt nicht um die Zeit."Start" und "Ende" sind nicht DateTime, sondern Offsets
"Start" und "Ende" sind Offsets zum
EstimatedShipDate
. Sie sind nicht sieDateTime
selbst . Dies beschreibt besser, was passiert, und reduziert die Komplexität erheblich.Keine Notwendigkeit für eine
interface
. Keine Notwendigkeit für eineRange
Klasse. Machen Sie keine Komplexität, bis Sie sicher sind, dass Sie sie brauchen. Ich vermute sehr, dass eine einzelne Klasse mit einem Konstruktor mit 3 optionalen Parametern die Dinge viel einfacher macht.Verwenden Sie einen einzelnen Konstruktor , der alle 3 Werte über optionale Parameter übergibt. Dies liefert den gesamten benötigten Kontext. Die Einzelkonstruktorlogik kann dann alle Variationen auswerten, um den Anfangszustand korrekt einzustellen. Verwenden Sie bei Bedarf auch benannte Parameter im Konstruktoraufruf, um dies kristallklar zu machen.
Tun Sie das nicht und die Dinge sind einfacher; Verwenden Sie stattdessen DateTime.AddDays () `.
Dies ist möglicherweise flexibler, wenn sich Datums- und Versatzwerte ändern dürfen.
Lesen Sie hier mehr über den Umgang mit Zeitzonen.
DateTime.DateTimeKind
Fügen Sie vorerst nur einen (enum) Konstruktorparameter hinzu und behandeln Sie ihn später.Aus einer der Antworten:
Verwenden Sie die
DateTime.Date
Eigenschaft und ignorieren Sie die Zeit vollständig.quelle
Was ist mit so etwas
Beide
EarliestShippingDate
undLatestShippingDate
sind auf das garantierte Versanddatum eingestellt.EarliestShippingDate
ist auf heute eingestellt undLatestShippingDate
ist auf eingestellttoday + (Y - X)
quelle
Sie müssen entscheiden, was der Unterschied (falls vorhanden) zwischen einem einzelnen Versanddatum und einem Bereich ist, der aus einem Tag besteht. Wenn ein "einzelnes Versanddatum" nur eine Reihe von Tagen ist, die zufällig aus einem Tag bestehen, modellieren Sie alles als Start- und Enddatum. Wenn ein "einzelnes Versanddatum" und eine Reihe von Tagen mit demselben Start- und Enddatum unterschiedlich behandelt werden sollen, speichern Sie einen der beiden Fälle, entweder ein einzelnes Datum für ein einzelnes Versanddatum und zwei Daten für eine Reihe von Tagen .
quelle
Sie haben grundsätzlich 2 Möglichkeiten:
2 Termine. Wenn das Objekt ein einzelnes Datum darstellt, setzen Sie entweder beide auf denselben Wert oder das zweite auf einen Nullwert.
1 Datum und eine Zeitspanne. Das Datum stellt den Start dar und die Zeitspanne zeigt, wie viel in der Zukunft der Bereich sein kann. Setzen Sie den Bereich für ein einzelnes Datum auf 0.
Für den Versand hätte ich eher ein Datum als ein Datum, da Sie ohne Zweifel die Lieferung am Morgen / Abend modellieren möchten. Welche der beiden Optionen am besten geeignet ist, hängt davon ab, wie Sie die Anzeige berechnen möchten. Wenn Sie "zwischen x und y" anzeigen, ist die erste Option möglicherweise einfacher zu verwenden. Wenn Sie "bis zu x Tage von y" anzeigen, ist letztere einfacher zu verwenden.
Wenn Sie Nullwerte nicht mögen, ist die letztere Option besser, da die Berechnung des ursprünglichen Datums plus der Zeitspanne unabhängig davon erfolgen kann, ob die Zeitspanne einen Wert hat oder auf 0 gesetzt ist. Sie erhalten immer ein korrektes Ergebnis, ohne nach Null zu suchen.
quelle