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:
- Poder expresivo
- 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 of
mé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 long
valor, 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 LocalDate
constructor 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.
ofEpochDay
Sin embargo, la API que llama explícitamente indica una clara diferencia en las unidades y la intención. Del mismo modo, LocalDate.ofYearDay
indica que month
se está omitiendo el parámetro común y que el parámetro del día, aunque sigue siendo un int
, dayOfYear
no 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 . LocalDate
podría proporcionar un constructor Java estándar además de, o en lugar de, sus dos of
métodos. ¿Pero con qué fin? Todavía necesita ofEpochDay
y ofDayYear
para esos casos de uso. Algunas clases proporcionan ambos constructores y el equivalente de ofXYZ
métodos, por ejemplo, ambos Float('3.14')
y Float.parseFloat('3.14')
existen. Pero si necesita ofXYZ
constructores 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 LocalDate
métodos son constructores. Hay siete grupos principales de métodos que construyen nuevas instancias (respectivamente, la at
, from
, minus
, of
, parse
,plus
y with
prefijos). De LocalDate
los 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.