Perché java.time ha metodi per creare oggetti invece di semplici costruttori?

9

Nel nuovo pacchetto java.time le classi core utilizzano il metodo factory of invece di un costruttore pubblico. Anche se mi piacciono i cosmetici del metodo of , non riesco a vedere una buona ragione per non utilizzare un costruttore. La documentazione che ho trovato spiega solo che questo è il caso e non entra nel vero perché è il caso.

Citazione da tutorial :

Most of the classes in the Date-Time API create objects that are immutable, meaning that, after the object is created, it cannot be modified. To alter the value of an immutable object, a new object must be constructed as a modified copy of the original. This also means that the Date-Time API is, by definition, thread-safe. This affects the API in that most of the methods used to create date or time objects are prefixed with of, from, or with, rather than constructors, and there are no set methods.

    
posta softarn 22.06.2016 - 13:30
fonte

3 risposte

9

I metodi di fabbrica consentono di semplificare i costruttori - la differenza quale istanza nel tempo è rappresentata dall'oggetto può essere separata dal calcolo dell'istante.

Ci sono molti metodi factory, alcuni che eseguono il parsing di stringhe, alcuni convertiti da qualche altra data o rappresentazione temporale, ma tutto ciò che può succedere prima l'oggetto deve essere creato (se si crea un nuovo oggetto è addirittura necessario - potrebbe anche essere memorizzato nella cache).

Un altro grande vantaggio è che i metodi factory possono avere nomi descrittivi : i metodi per l'analisi delle rappresentazioni String sono chiamati per es. LocalTime.parse : non è possibile con i costruttori.

    
risposta data 22.06.2016 - 15:17
fonte
8

Non è sempre necessario creare un nuovo oggetto quando si ottiene un valore di un tipo immutabile. Un metodo factory può restituire un oggetto che è già stato creato mentre una chiamata del costruttore crea sempre un nuovo oggetto.

Ci sono altri vantaggi rispetto ai metodi di fabbrica, ma non sono strettamente correlati all'immutabilità e all'API Date-Time. Ad esempio, i metodi factory hanno nomi e possono restituire oggetti di alcuni sottotipi.

    
risposta data 22.06.2016 - 15:15
fonte
5

Non ho progettato o scritto l'API Date-Time di Java, quindi non posso dire definitivamente "perché lo hanno fatto in questo modo". Posso tuttavia suggerire due motivi chiave per cui dovrebbero farlo in questo modo:

  1. Potere espressivo
  2. Modulo parallelo

Innanzitutto, considera il contesto: le date e il time code di Java sono un pacchetto ampio e complesso che si occupa di un argomento difficile molto . Comune quanto il "tempo", l'implementazione del concetto implica decine di interpretazioni e formati, con variazioni su posizioni geografiche (fusi orari), lingue, sistemi di calendario, risoluzioni di clock, scale temporali, rappresentazioni numeriche, fonti di dati e intervalli. I delta correttivi (ad esempio anni bisestili / giorni) sono comuni e possono essere inseriti in modo irregolare in qualsiasi momento (secondi bisestili). Tuttavia, il pacchetto è una caratteristica fondamentale, che probabilmente sarà ampiamente utilizzata, e ci si aspetta che affronti tutte queste variazioni in modo corretto e preciso. È un compito arduo. Il risultato, non sorprendentemente, è una API complessa che include molte classi, ognuna con molti metodi.

Ora, perché usare i metodi of piuttosto che un costruttore di classi "normale"? Per esempio , perché LocalDate.of(...) , LocalDate.ofEpochDay(...) e LocalDate.ofYearDay(...) piuttosto che solo una manciata di chiamate new LocalDate(...) ?

Primo, potere espressivo : i singoli metodi denominati esprimono l' intento della costruzione e il modulo dei dati che vengono consumati.

Supponiamo per un momento che l' sovraccarico del metodo di Java sia sufficiente per consentire la varietà di costruttori che si desidera. (Senza entrare in una discussione sulle funzionalità linguistiche in digitazione anatra e single vs. dynamic vs. dispatch multiplo Dirò solo: a volte questo è vero, a volte no. Per ora, presumi solo che non ci siano limitazioni tecniche.)

Potrebbe essere possibile che la documentazione del costruttore dichiari "quando viene passato un solo valore long , quel valore viene interpretato come un conteggio del giorno dell'epoca, non un anno." Se i tipi di dati di basso livello passati agli altri costruttori sono diversi (ad esempio solo il caso epoch usa long , e tutti gli altri costruttori usano int ), potresti farla franca.

Se hai guardato il codice chiamando un costruttore LocalDate con un singolo long , sembrerebbe molto simile al codice in cui è passato un anno, specialmente con il passare di un anno probabilmente il caso più comune. Avere quel costruttore comune potrebbe essere possibile, ma non porterebbe necessariamente a una grande chiarezza.

L'API che richiama esplicitamente ofEpochDay tuttavia segnala una netta differenza tra unità e intento. Allo stesso modo LocalDate.ofYearDay segnala che il parametro comune month viene saltato e che il parametro giorno, mentre è ancora un int , è un dayOfYear non un dayOfMonth . I metodi di costruzione espliciti hanno lo scopo di esprimere ciò che sta accadendo con maggiore chiarezza.

Linguaggi digitati in modo dinamico e anatra come Python e JavaScript hanno altri mezzi per segnalare interpretazioni alternative (ad es. parametri opzionali e out-of-band valori sentinella ), ma anche gli sviluppatori Python e JS utilizzano spesso nomi di metodi distinti per segnalare diverse interpretazioni. È ancora più comune in Java, dati i suoi limiti tecnici (è un linguaggio tipizzato in modo statico, a una sola uscita) e le sue convenzioni / espressioni culturali.

Secondo, modulo parallelo . LocalDate potrebbe fornire un costruttore Java standard in aggiunta o in sostituzione dei suoi due metodi of . Ma a quale fine? Ha ancora bisogno di ofEpochDay e ofDayYear per questi casi d'uso. Alcune classi forniscono entrambi i costruttori e l'equivalente dei metodi ofXYZ ; ad esempio, esistono sia Float('3.14') che Float.parseFloat('3.14') . Ma se hai bisogno di ofXYZ di costruttori per alcune cose, perché non usarli tutto il tempo? È più semplice, più regolare e più parallelo. Come tipo immutabile, i maggioranza di LocalDate metodi sono costruttori. Esistono sette principali gruppi di metodi che costruiscono nuove istanze (rispettivamente, at , from , minus , of , parse , plus e with prefissi). Dei metodi 60+ di LocalDate , 35+ sono costruttori di fatto . Solo 2 di questi potrebbero essere facilmente convertiti in costruttori basati su classi standard. Ha davvero senso, in casi speciali, quei 2 in un modo non parallelo a tutti gli altri? Il codice client che utilizza questi due casi speciali sarà notevolmente più chiaro o più semplice? Probabilmente no. Potrebbe persino rendere il codice client più vario e meno diretto.

    
risposta data 22.06.2016 - 18:15
fonte

Leggi altre domande sui tag