Por que o java.time possui métodos para criar objetos em vez de apenas construtores?

8

No novo pacote java.time, as classes principais estão usando o método factory em ofvez de um construtor público. Mesmo gostando dos cosméticos do ofmétodo, não vejo uma boa razão para não usar um construtor. A documentação que encontrei explica apenas que esse é o caso e não explica exatamente o motivo.

Citação do tutorial :

A maioria das classes na API Date-Time cria objetos imutáveis, o que significa que, após a criação do objeto, ele não pode ser modificado. Para alterar o valor de um objeto imutável, um novo objeto deve ser construído como uma cópia modificada do original. Isso também significa que a API Date-Time é, por definição, segura para threads. Isso afeta a API, pois a maioria dos métodos usados ​​para criar objetos de data ou hora são prefixados com de, de ou com, em vez de construtores, e não há métodos definidos.

softarn
fonte

Respostas:

9

Os métodos de fábrica permitem simplificar os construtores - a diferença de qual instância no tempo é representada pelo objeto pode ser separada do cálculo do instante.

Existem muitos métodos de fábrica, alguns analisando String, outros convertendo a partir de outra representação de data ou hora, mas tudo o que pode acontecer antes que o Objeto precise ser criado (se a criação de um novo Objeto é necessária) - também pode ser uma em cache).

Outra grande vantagem é que os métodos de fábrica podem ter nomes descritivos : os métodos para analisar representações de String são nomeados, por exemplo LocalTime.parse- isso não é possível com construtores.

Hulk
fonte
Para melhorar esta resposta, você pode adicionar um exemplo do objeto Month, por exemplo, que escolhe qual instância retornar no método of factory.
softarn
8

Nem sempre é necessário criar um novo objeto ao obter um valor de um tipo imutável. Um método de fábrica pode retornar um objeto que já foi criado enquanto uma chamada de construtor sempre cria um novo objeto.

Existem outras vantagens nos métodos de fábrica, mas eles não estão tão intimamente relacionados à imutabilidade quanto à API Date-Time. Por exemplo, os métodos de fábrica têm nomes e podem retornar objetos de algum subtipo.

VEM DE ONDE
fonte
5

Não projetei ou escrevi a API de data e hora do Java, por isso não posso dizer definitivamente "por que eles fizeram dessa maneira". No entanto, posso sugerir dois motivos principais para que eles façam dessa maneira:

  1. Poder expressivo
  2. Forma paralela

Primeiro, considere o contexto: o código de datas e hora do Java é um pacote grande e complexo que trata de um assunto muito complicado. Por mais comum que seja a "hora", a implementação do conceito envolve dezenas de interpretações e formatos, com variações entre locais geográficos (fusos horários), idiomas, sistemas de calendário, resoluções de relógio, escalas de tempo, escalas de tempo, representações numéricas, fontes de dados e intervalos. Deltas corretivos (por exemplo, anos bissextos / dias) são comuns e podem ser inseridos irregularmente a qualquer momento (segundos bissextos). No entanto, o pacote é um recurso essencial, provavelmente usado amplamente, e espera-se que ele lide com todas essas variações de maneira correta e precisa. É uma tarefa difícil. O resultado, sem surpresa, é uma API complexa, incluindo muitas classes, cada uma com muitos métodos.

Agora, por que usar ofmétodos em vez de um construtor de classe "regular"? Por exemplo , por que LocalDate.of(...), LocalDate.ofEpochDay(...)e LocalDate.ofYearDay(...)não apenas um punhado de new LocalDate(...)chamadas?

Primeiro, poder expressivo : métodos nomeados individuais expressam a intenção da construção e a forma dos dados que estão sendo consumidos.

Suponha por um momento que a sobrecarga do método Java seja suficiente para permitir a variedade de construtores que você deseja. (Sem entrar em uma discussão sobre o recurso de linguagem sobre digitação de patos e envio único versus dinâmico versus envio múltiplo , vou apenas dizer: às vezes isso é verdade, às vezes não. Por enquanto, apenas assuma que não há nenhuma limitação técnica.)

Pode ser possível para a documentação do construtor declarar "quando passado apenas um único longvalor, esse valor é interpretado como uma contagem de dias da época, não um ano". Se os tipos de dados de baixo nível passados ​​para os outros construtores forem diferentes (por exemplo, apenas o caso da época usa longe todos os outros construtores usam int), você pode se safar disso.

Se você analisasse o código que chama um LocalDateconstrutor com um único long, seria muito semelhante ao código no qual um ano é passado, especialmente com a passagem de um ano, sem dúvida o caso mais comum. Ter esse construtor comum pode ser possível, mas não necessariamente leva a uma grande clareza.

A API explicitamente chamando no ofEpochDayentanto, sinaliza uma clara diferença de unidades e intenção. Da mesma forma, LocalDate.ofYearDaysinaliza que o monthparâmetro comum está sendo ignorado e que o parâmetro day, embora ainda intseja um, dayOfYearnão é um dayOfMonth. Os métodos explícitos do construtor visam expressar o que está acontecendo com maior clareza.

Linguagens dinamicamente e tipadas como pato, como Python e JavaScript, têm outros meios para sinalizar interpretações alternativas (por exemplo, parâmetros opcionais e valores de sentinela fora da banda ), mas mesmo os desenvolvedores de Python e JS costumam usar nomes de métodos distintos para sinalizar interpretações diferentes. É ainda mais comum em Java, dadas as restrições técnicas (é uma linguagem de envio único estaticamente) e as convenções / expressões culturais.

Segunda forma paralela . LocalDatepoderia fornecer um construtor Java padrão, além de, ou no lugar de, seus dois ofmétodos. Mas com que finalidade? Ele ainda precisa ofEpochDaye ofDayYearpara esses casos de uso. Algumas classes fornecem construtores e o equivalente a ofXYZmétodos - por exemplo, ambos Float('3.14')e Float.parseFloat('3.14')existem. Mas se você precisa de ofXYZconstrutores para algumas coisas, por que não usá-las o tempo todo? Isso é mais simples, mais regular e mais paralelo. Como um tipo imutável, a maioria dos LocalDatemétodos são construtores. Existem grandes grupos de sete de métodos que constroem novos casos (respectivamente, o at, from, minus, of, parse,pluse withprefixos). Dos LocalDatemais de 60 métodos, 35+ são construtores de fato . Apenas dois deles podem ser facilmente convertidos em construtores padrão baseados em classe. Realmente faz sentido, para casos especiais, aqueles 2 de uma maneira não paralela a todos os outros? O código do cliente usando esses dois construtores de casos especiais seria notavelmente mais claro ou mais simples? Provavelmente não. Pode até tornar o código do cliente mais variado e menos direto.

Jonathan Eunice
fonte