Una proprietà che può rappresentare sia una singola data che un intervallo di date: come modellarlo correttamente?

8

Lavoro in un sistema che può rappresentare una "stima di spedizione" in due modi:

  1. Una data specifica: l'articolo è garantito per la spedizione in tale data
  2. Intervallo di un giorno: l'articolo verrà spedito "da X a Y" giorni a partire da oggi

Le informazioni sul modello sono semanticamente uguali, è "la stima di spedizione". Quando ottengo le informazioni sulla stima di spedizione dal sistema, posso dire se la stima è della prima forma o della seconda forma.

Il modello attuale per questo è simile al seguente:

class EstimattedShippingDateDetails
{
    DateTime? EstimattedShippingDate {get; set;}
    Range? EstimattedShippingDayRange {get; set;}
}

Range è una semplice classe per racchiudere un "inizio - > fine" di numeri interi, un po 'come questo:

struct Range
{
    int Start {get; set}
    int End {get; set}

    public override ToString()
    {
        return String.Format("{0} - {1}", Start, End);
    }
}

Non mi piace questo approccio perché solo una delle proprietà sul modello di stima sarà mai popolata, e ho bisogno di testare null su uno di essi e assumere che l'altro abbia i dati.

Ciascuna delle proprietà viene mostrata in modo diverso all'utente, ma nello stesso punto dell'interfaccia utente, utilizzando un MVC DisplayTemplate personalizzato, in cui risiede la logica di commutazione corrente:

@Model EstimattedShippingDateDetails

@if (Model.EstimattedShippingDate.HasValue)
{
    Html.DisplayFor(m => Model.EstimattedShippingDate)
}
else
{
    Html.DisplayFor(m => Model.EstimattedShippingDayRange)
}

Come potrei modellarlo per renderlo più rappresentativo del requisito reale, pur mantenendo la logica del display semplice in un'applicazione MVC?

Ho pensato di utilizzare un'interfaccia e due implementazioni, una per ogni "tipo" di stima, ma non riesco a spiegarmi come un'interfaccia comune per entrambi. Se creo un'interfaccia senza membri, quindi non posso accedere ai dati in modo unificato ed è un cattivo design IMHO. Volevo anche mantenere il viewmodel il più semplice possibile. Avrei comunque ottenuto un codice "corretto per costruzione" con questo approccio, poiché non ci sarebbe più bisogno di nullable: ogni implementazione avrebbe una proprietà non nullable, o DateTime o Range .

Ho anche considerato di utilizzare solo un Range e quando si verifica la situazione # 1, usa lo stesso DateTime per Start e End , ma ciò aggiungerebbe complicazioni su come emettere i valori sul L'interfaccia utente dovrebbe quindi rilevare se si tratta di un errore statico o di un intervallo confrontando i valori e formattare correttamente l'intervallo in modo che venga visualizzato come una singola data o un intervallo formattato.

Sembra che quello di cui ho bisogno è un concetto simile ai sindacati di Typescript: fondamentalmente una proprietà che può essere di due tipi. Nessuna cosa del genere esiste in modo nativo in C # ovviamente (solo dynamic sarebbe vicino a quello).

    
posta julealgon 10.03.2016 - 01:45
fonte

7 risposte

17

Utilizza un intervallo di date (ad esempio due date) per tutte le stime di spedizione.

Per una singola data, imposta X e Y uguali.

    
risposta data 10.03.2016 - 03:41
fonte
2

Puoi modellarlo usando l'incapsulamento.

Lascia che la classe abbia 2 costruttori, uno per la singola data e un altro per l'intervallo di date.

Nel metodo ToString () determina lo 'stato' della classe e genera la stringa formattata appropriata.

Aggiungi altri metodi appropriati per le altre tue esigenze.

    
risposta data 10.03.2016 - 02:14
fonte
1

Questo è un problema abbastanza comune, Nullables per esempio risolve il problema del posto comune di endDates per cose che non sono terminate.

Ma penso che tu abbia scelto un cattivo esempio.

Con il tuo caso esatto di due date, un intervallo di date che inizia di mattina e finisce di sera sembra essere la soluzione perfetta. O perpassa una data di inizio e un numero intero di giorni extra che potrebbe essere?

Tuttavia, consideriamo un caso più difficile. Ho due tipi di consegna, posta e ritiro dal negozio. Questi sono obvs molto più diversi, il negozio ha bisogno del nome e dell'indirizzo, il post ha un costo, può comprendere una serie di opzioni di consegna, codici di tracciabilità ecc.

L'approccio standard è cercare le cose comuni che rendono entrambe queste "opzioni di consegna" e metterle in una classe base. La sottoclasse dei due casi specifici con i dettagli aggiuntivi di cui hanno / bisogno.

In quasi tutti i casi avrai almeno un Id, un Tipo e una Descrizione comuni ad entrambi i tipi. Quindi:

public class DeliveryOption 
{
     Public string Id;
     Public typeEnum Type;
     public string Description;
}


Public class Collection : DeliveryOption
{
     Public string ShopName;
}

Public class Post : DeliveryOption
{
     Public DateTime EstDelivery;
}
    
risposta data 10.03.2016 - 09:50
fonte
1

"start" e "end" non sono DateTime, Offset

How could I model this to make it more representative of the actual requirement while still keeping the display logic simple in an MVC application?

"start" e "end" sono offset alla EstimatedShipDate . Non sono DateTime s stessi . Questo descrive meglio cosa sta accadendo e ridurrà drasticamente la complessità.

Non è necessario un interface . Non c'è bisogno di una classe Range . Non fare complessità finché non sei sicuro di averne bisogno. Ho il strong sospetto che una singola classe con un costruttore di parametri a 3 facoltativi manterrà le cose molto più semplici.

The data I get from the server is either a DateTime object, representing the expected shipping date, or two integer values with the minimum and maximum days from today that the item will be shipped.

Utilizza un singolo costruttore passando tutti e 3 i valori tramite parametri opzionali. Questo fornisce tutto il contesto necessario. La logica del costruttore singolo può quindi valutare tutte le variazioni per impostare correttamente lo stato iniziale. Utilizza anche i parametri denominati nella chiamata del costruttore, se lo desideri, per renderlo cristallino.

public class ShipDate {
    public ShipDate (Datetime? shipDate = null, int earliestOffset = 0, latestOffset = 0) {
        EstShipDate = (DateTime) shipDate ?? DateTime.Now.Date;
        start = earliestOffset < 0 ? 0 : earliestOffset;
        end   = latestOffset < 0 ? 0 : latestOffset;
        // That's all, folks!
    }
}

If I standardize on dates, I'll have to convert those integers into properly constructed DateTimes,

No. Non farlo e le cose sono più semplici; usa invece DateTime.AddDays () '.

public DateTime EstShipDate      {get; protected set;}
public DateTime EarliestShipDate { get { return EstShipDate.AddDays(start).Date; } }
public DateTime LatestShipDate   { get { return EstShipDate.AddDays(end).Date; } }

Questo è potenzialmente più flessibile se le date e i valori di offset possono cambiare.

which would raise the complexity quite a bit due to everything that would need to be taken into account, like client vs server dates, UTC, daylight saving time differences etc. I'd like to avoid creating this kind of complexity at this point.

Leggi come gestire i fusi orari qui.

Per ora basta includere un parametro di costruzione DateTime.DateTimeKind (enum) e trattarlo in seguito.

Da una delle risposte:

With your exact case of two dates, a date range which starts in the morning and ends in the evening seems to be the perfect solution. Or perhpas a start date and an integer number of extra days it might be?

Utilizza la proprietà DateTime.Date e ignora completamente il tempo.

    
risposta data 14.03.2016 - 00:10
fonte
0

Che dire di qualcosa

class EstimattedShippingDateDetails
{
    DateTime EarliestShippingDate {get; set;}
    DateTime LatestShippingDate {get; set;}
    TimeSpan Range 
    {
        get 
        { 
            return LatestShippingDate - EarliestShippingDate; 
        } 
    }
}

A specific date: The item is guaranteed to ship at that date

Sia EarliestShippingDate che LatestShippingDate sono impostati sulla data di spedizione garantita.

A day interval: The item will be shipped "X to Y" days from today

EarliestShippingDate è impostato su oggi e LatestShippingDate è impostato su today + (Y - X)

    
risposta data 10.03.2016 - 14:47
fonte
0

Devi decidere qual è la differenza (se esiste) tra una singola data di spedizione e un intervallo che consiste in un giorno. Se una "data di spedizione singola" è solo un intervallo di giorni che consiste in un giorno, quindi modellare tutto come data di inizio e data di fine. Se una "data di spedizione singola" e un intervallo di giorni con la stessa data di inizio e di fine devono essere trattati diversamente, memorizzare uno dei due casi, una singola data per una singola data di spedizione e due date per un intervallo di giorni .

    
risposta data 10.03.2016 - 16:26
fonte
0

Hai sostanzialmente 2 opzioni:

  • 2 date. Se l'oggetto rappresenta una singola data, impostali entrambi sullo stesso valore oppure imposta il 2 ° su un valore nullo.

  • 1 data e un intervallo di tempo. La data rappresenta l'inizio e l'intervallo mostra quanto in futuro può essere l'intervallo. Imposta l'intervallo su 0 per una singola data.

Per la spedizione avrei un tempo di consegna anziché una data, poiché senza dubbio vorrai modellare la consegna mattina / sera. Quale delle due opzioni è la migliore dipende da come ti piace calcolare il display. Se stai visualizzando "tra xey", la prima opzione potrebbe essere più facile da usare, se stai mostrando "fino a x giorni da y", quest'ultimo è più facile da usare.

Se non ti piacciono i valori nulli, allora quest'ultima opzione è migliore, poiché il calcolo della data originale più l'intervallo può essere fatto indipendentemente dal fatto che l'intervallo abbia un valore o sia impostato su 0. Otterrai sempre un risultato corretto senza cercare nullo.

    
risposta data 10.03.2016 - 17:04
fonte

Leggi altre domande sui tag