Trabajo en un sistema que puede representar una "estimación de envío" de dos maneras:
- Una fecha específica: el artículo está garantizado para enviarse en esa fecha
- Intervalo de un día: el artículo se enviará en días "X a Y" a partir de hoy
La información sobre el modelo es semánticamente la misma, es "la estimación de envío". Cuando obtengo la información sobre la estimación de envío del sistema, puedo decir si la estimación es de la primera forma o la segunda forma.
El modelo actual para esto es similar al siguiente:
class EstimattedShippingDateDetails
{
DateTime? EstimattedShippingDate {get; set;}
Range? EstimattedShippingDayRange {get; set;}
}
Range
es una clase simple para envolver un "principio -> fin" de enteros, algo así:
struct Range
{
int Start {get; set}
int End {get; set}
public override ToString()
{
return String.Format("{0} - {1}", Start, End);
}
}
No me gusta este enfoque porque solo se completará una de las propiedades en el modelo de estimación, y necesito probar nulo en uno de ellos y asumir que el otro tiene los datos.
Cada una de las propiedades se muestra de manera diferente para el usuario, pero en el mismo lugar en la interfaz de usuario, utilizando una MVC DisplayTemplate personalizada, donde reside la lógica de conmutación actual:
@Model EstimattedShippingDateDetails
@if (Model.EstimattedShippingDate.HasValue)
{
Html.DisplayFor(m => Model.EstimattedShippingDate)
}
else
{
Html.DisplayFor(m => Model.EstimattedShippingDayRange)
}
¿Cómo podría modelar esto para hacerlo más representativo del requisito real y al mismo tiempo mantener la lógica de visualización simple en una aplicación MVC?
Pensé en usar una interfaz y dos implementaciones, una para cada "tipo" de estimación, pero parece que no puedo entender una interfaz común para ambas. Si creo una interfaz sin ningún miembro, entonces no puedo acceder a los datos de manera unificada y es un mal diseño en mi humilde opinión. También quería mantener el modelo de vista lo más simple posible. Sin embargo, obtendría un código "correcto por construcción" con este enfoque, ya que ya no sería necesario anular: cada implementación tendría una propiedad no anulable, ya sea a DateTime
o a Range
.
También consideré usar solo uno Range
y cuando ocurre la situación n. ° 1, solo use el mismo DateTime
para ambos Start
y End
, pero esto agregaría complicaciones sobre cómo emitir los valores a la interfaz de usuario, ya que luego tendría que detectar si es estático o intervalo por comparar los valores y formatear correctamente el rango para que se muestre como una sola fecha o un intervalo formateado.
Parece que lo que necesito es un concepto similar a las uniones de Typecript: básicamente una propiedad que puede ser de dos tipos. No existe tal cosa de forma nativa en C #, por supuesto (solo dynamic
estaría cerca de eso).
fuente
Respuestas:
Use un rango de fechas (es decir, dos fechas) para todas las estimaciones de envío.
Para una sola fecha, haga que X e Y sean iguales.
fuente
DateTime
objeto, que representa la fecha de envío esperada, o dos valores enteros con los días mínimo y máximo a partir de hoy que se enviará el artículo. Si estandarizo las fechas, tendré que convertir esos enteros en DateTimes correctamente construidos, lo que aumentaría bastante la complejidad debido a todo lo que debería tenerse en cuenta, como las fechas cliente vs servidor, UTC, horario de verano diferencias, etc. Me gustaría evitar crear este tipo de complejidad en este momento.Puede modelar esto usando encapsulación.
Deje que la clase tenga 2 constructores, uno para la fecha única y otro para el rango de fechas.
En el método ToString (), determine el 'estado' de la clase y genere la cadena formateada adecuada.
Agregue otros métodos según corresponda para sus otras necesidades.
fuente
Range
tipo podría usarse en otros lugares del sistema y, en ese caso, mostrarse de la misma manera. Es por eso que necesito centralizar laRange
pantalla en una DisplayTemplate que se puede reutilizar. Si uso elToString
método para encapsular eso, estoy perdiendo mucha flexibilidad que proporcionan las vistas.state
para ..ToString () [para] determinar el .ToString()
debería simplemente "informar" el estado. El estado se determina en constructores, establecedores de propiedades, etc. Y, simplemente, no veo nada en el OP que sugiera que hay o debería haber un "estado resumen"Este es un problema bastante común, Nullables, por ejemplo, resuelve el problema del lugar común de endDates para cosas que no han terminado.
Pero creo que has elegido un mal ejemplo.
Con su caso exacto de dos fechas, un intervalo de fechas que comienza en la mañana y termina en la noche parece ser la solución perfecta. ¿O quizás una fecha de inicio y un número entero de días adicionales que podría ser?
Sin embargo, consideremos un caso más difícil. Tengo dos tipos de entrega, publicación y recogida en la tienda. Estos son obviamente mucho más diferentes, la tienda necesita el nombre y la dirección, la publicación tiene un costo, tal vez una serie de opciones de entrega, códigos de seguimiento, etc.
El enfoque estándar es buscar las cosas comunes que hacen estas dos 'opciones de entrega' y ponerlas en una clase base. La subclase los dos casos específicos con los detalles adicionales que tienen / necesitan.
En casi todos los casos, tendrá al menos un Id, un Tipo y una Descripción común a ambos tipos. Entonces:
fuente
DataTime.Date
propiedad y no te preocupes por el tiempo."inicio" y "fin" no son DateTime, son compensaciones
"inicio" y "fin" son compensaciones del
EstimatedShipDate
. No sonDateTime
ellos mismos . Esto describe mejor lo que está sucediendo y reducirá drásticamente la complejidad.No hay necesidad de un
interface
. No hay necesidad de unaRange
clase. No haga complejidad hasta que esté seguro de que la necesita. Sospecho firmemente que una sola clase con un constructor de 3 parámetros opcionales mantendrá las cosas mucho más simples.Use un solo constructor pasando los 3 valores a través de parámetros opcionales. Esto proporciona todo el contexto necesario. La lógica del constructor único puede evaluar todas las variaciones para establecer el estado inicial correctamente. También use parámetros con nombre en la llamada al constructor si lo desea para que quede claro como el cristal.
No. No hagas esto y las cosas son más simples; en su lugar use DateTime.AddDays () `.
Esto es potencialmente más flexible si se permite cambiar las fechas y los valores de desplazamiento.
Lea sobre el manejo de zonas horarias aquí.
Por ahora solo incluya un
DateTime.DateTimeKind
parámetro de constructor (enum) y trátelo más tarde.De una de las respuestas:
Usa la
DateTime.Date
propiedad e ignora totalmente el tiempo.fuente
¿Qué tal algo como
Ambos
EarliestShippingDate
yLatestShippingDate
están configurados para la fecha de envío garantizada.EarliestShippingDate
se establece en la actualidad yLatestShippingDate
se establece entoday + (Y - X)
fuente
Debe decidir cuál es la diferencia (si la hay) entre una sola fecha de envío y un rango que consta de un día. Si una "fecha de envío única" es solo un rango de días que consta de un día, entonces modele todo como fecha de inicio y fecha de finalización. Si una "fecha de envío única" y un rango de días con la misma fecha de inicio y finalización deben tratarse de manera diferente, almacene uno de los dos casos, ya sea una fecha única para una fecha de envío única y dos fechas para un rango de días .
fuente
Básicamente tienes 2 opciones:
2 fechas. Si el objeto representa una sola fecha, configúrelas en el mismo valor o configure la segunda en un valor nulo.
1 fecha y un intervalo de tiempo. La fecha representa el inicio, y el intervalo de tiempo muestra cuánto en el futuro puede ser el rango. Establezca el rango en 0 para una sola fecha.
Para el envío, tendría una fecha y hora en lugar de una fecha, ya que sin duda querrá modelar la entrega por la mañana / tarde. Cuál de las 2 opciones es mejor depende de cómo desee calcular la pantalla. Si está mostrando "entre x e y", entonces la primera opción podría ser más fácil de usar, si está mostrando "hasta x días a partir de y", entonces la última es más fácil de usar.
Si no le gustan los valores nulos, entonces la última opción es mejor, ya que el cálculo de la fecha original más el intervalo de tiempo se puede hacer independientemente de si el intervalo de tiempo tiene un valor o se establece en 0. Siempre obtendrá un resultado correcto sin verificar si es nulo.
fuente