¿Por qué java.time tiene métodos para crear objetos en lugar de solo constructores?

8

En el nuevo paquete java.time, las clases principales están utilizando el método de fábrica en oflugar de un constructor público. Aunque me gustan los cosméticos del ofmétodo, no puedo ver una buena razón para no usar un constructor. La documentación que he encontrado solo explica que este es el caso y realmente no explica por qué es así.

Cita del tutorial :

La mayoría de las clases en la API de fecha y hora crean objetos que son inmutables, lo que significa que, después de crear el objeto, no se puede modificar. Para alterar el valor de un objeto inmutable, se debe construir un nuevo objeto como una copia modificada del original. Esto también significa que la API de fecha y hora es, por definición, segura para subprocesos. Esto afecta a la API ya que la mayoría de los métodos utilizados para crear objetos de fecha u hora tienen el prefijo de, desde o con, en lugar de constructores, y no hay métodos establecidos.

softarn
fuente

Respuestas:

9

Los métodos de fábrica permiten simplificar los constructores: la diferencia de qué instancia en el tiempo está representada por el Objeto se puede separar del cálculo del instante.

Hay muchos métodos de fábrica, algunos haciendo análisis de cadenas, algunos convirtiendo desde otra representación de fecha u hora, pero todo eso puede suceder antes de que se tenga que crear el Objeto (si es necesario crear un nuevo Objeto, también podría ser un uno en caché).

Otra gran ventaja es que los métodos de fábrica pueden tener nombres descriptivos : los métodos para analizar representaciones de cadenas se denominan, por ejemplo LocalTime.parse, eso no es posible con los constructores.

Casco
fuente
Para mejorar esta respuesta, puede agregar un ejemplo de, por ejemplo, un objeto Month que elija qué instancia devolver en el método de fábrica.
softarn
8

No siempre es necesario crear un nuevo objeto al obtener un valor de tipo inmutable. Un método de fábrica puede devolver un objeto que ya se ha creado, mientras que una llamada de constructor siempre crea un nuevo objeto.

Los métodos de fábrica tienen otras ventajas, pero no están tan estrechamente relacionados con la inmutabilidad y la API de fecha y hora. Por ejemplo, los métodos de fábrica tienen nombres y pueden devolver objetos de algún subtipo.

VIENE DE
fuente
5

No diseñé ni escribí la API de fecha y hora de Java, por lo que definitivamente no puedo decir "por qué lo hicieron de esta manera". Sin embargo, puedo sugerir dos razones clave por las que deberían hacerlo de esta manera:

  1. Poder expresivo
  2. Forma paralela

Primero, considere el contexto: el código de fecha y hora de Java es un paquete grande y complejo que trata un tema muy complicado. Tan común como el "tiempo" es, la implementación del concepto involucra docenas de interpretaciones y formatos, con variaciones entre ubicaciones geográficas (zonas horarias), idiomas, sistemas de calendario, resoluciones de reloj, escalas de tiempo, representaciones numéricas, fuentes de datos e intervalos. Los deltas correctivos (p. Ej., Años bisiestos / días) son comunes y se pueden insertar de forma irregular en cualquier momento (segundos bisiestos). Sin embargo, el paquete es una característica central, que probablemente se utilizará ampliamente, y se espera que aborde todas esas variaciones de forma correcta y precisa. Es una tarea difícil. El resultado, no es sorprendente, es una API compleja que incluye muchas clases, cada una con muchos métodos.

Ahora, ¿por qué usar ofmétodos en lugar de un constructor de clase "regular"? Por ejemplo , ¿por qué LocalDate.of(...), LocalDate.ofEpochDay(...)y LocalDate.ofYearDay(...)en lugar de sólo un puñado de new LocalDate(...)llamadas?

Primero, el poder expresivo : los métodos con nombre individuales expresan la intención de la construcción y la forma de los datos que se consumen.

Suponga por un momento que la sobrecarga de métodos de Java es suficiente para permitir la variedad de constructores que desea. (Sin entrar en una discusión sobre las características del lenguaje sobre el tipeo de patos y el despacho simple vs. dinámico vs. múltiple , solo diré: a veces esto es cierto, a veces no. Por ahora, solo asuma que no hay limitación técnica).

Es posible que la documentación del constructor establezca "cuando se pasa un solo longvalor, ese valor se interpreta como un recuento de días de época, no un año". Si los tipos de datos de bajo nivel pasados ​​a los otros constructores son diferentes (por ejemplo, solo el caso de la época usa long, y todos los otros constructores usan int), puede salirse con la suya.

Si observara el código que llama a un LocalDateconstructor con un solo long, se vería muy similar al código en el que se pasa un año, especialmente con pasar un año posiblemente el caso más común. Tener ese constructor común podría ser posible, pero no necesariamente conduciría a una gran claridad.

ofEpochDaySin embargo, la API que llama explícitamente indica una clara diferencia en las unidades y la intención. Del mismo modo, LocalDate.ofYearDayindica que monthse está omitiendo el parámetro común y que el parámetro del día, aunque sigue siendo un int, dayOfYearno es un dayOfMonth. Los métodos de construcción explícitos están destinados a expresar lo que está sucediendo con mayor claridad.

Los lenguajes dinámicos y de tipo pato como Python y JavaScript tienen otros medios para señalar interpretaciones alternativas (por ejemplo, parámetros opcionales y valores centinela fuera de banda ), pero incluso los desarrolladores de Python y JS a menudo usan nombres de métodos distinguidos para señalar diferentes interpretaciones. Es aún más común en Java, debido a sus limitaciones técnicas (es un lenguaje de envío único de tipo estático) y sus convenciones / modismos culturales.

Segundo, forma paralela . LocalDatepodría proporcionar un constructor Java estándar además de, o en lugar de, sus dos ofmétodos. ¿Pero con qué fin? Todavía necesita ofEpochDayy ofDayYearpara esos casos de uso. Algunas clases proporcionan ambos constructores y el equivalente de ofXYZmétodos, por ejemplo, ambos Float('3.14')y Float.parseFloat('3.14')existen. Pero si necesita ofXYZconstructores para algunas cosas, ¿por qué no usarlas todo el tiempo? Eso es más simple, más regular y más paralelo. Como un tipo inmutable, la mayoría de los LocalDatemétodos son constructores. Hay siete grupos principales de métodos que construyen nuevas instancias (respectivamente, la at, from, minus, of, parse,plusy withprefijos). De LocalDatelos más de 60 métodos, más de 35 son constructores de facto . Solo 2 de ellos podrían convertirse fácilmente en constructores estándar basados ​​en clases. ¿Realmente tiene sentido hacer un caso especial de esos 2 de una manera no paralela a todos los demás? ¿El código del cliente que usa esos dos constructores de casos especiales sería notablemente más claro o más simple? Probablemente no. Incluso podría hacer que el código del cliente sea más variado y menos directo.

Jonathan Eunice
fuente