Perché usare i getter solo al contrario di contrassegnare le cose finali? [duplicare]

18

Lavoro in un ruolo java dopo aver lavorato per un paio d'anni nella programmazione funzionale. La nostra azienda è stata acquistata da google e ho acquisito un ruolo java dopo l'acquisizione.

Tornando a Java come sviluppatore poliglotta, sto generando getter per oggetti immutabili e sto vedendo che è una sciocchezza totale.

Ci sono motivi per cui la convenzione 'getField' dovrebbe essere usata in modo così prolifico? A me sembra quasi orribile a questo punto che così tante biblioteche si aspettino che i metodi getter pubblici lavorino con le loro funzionalità quando semplicemente rendere pubblico un campo e la finale avrebbero lo stesso effetto di rendere solo un getter su un campo pubblico mutabile.

Perché non è più una pratica comune abbandonare i setter e solo esporre un campo finale?

EDIT: Non penso che questo sia effettivamente il forum giusto in quanto penso che questo sia un punto di discussione piuttosto che una risposta dopo aver valutato le prospettive.

    
posta JasonG 21.07.2014 - 00:58
fonte

12 risposte

29

Per flessibilità - questo ha a che fare con ciò che succede quando cambi la classe in seguito.

Se foo.x è un membro pubblicamente accessibile (anche finale), e tu decidi che non hai più bisogno di quel membro, il codice che accede a quel metodo è ora rotto.

Se hai fornito solo un getter, puoi sempre fornire una versione di compatibilità di quel getter che calcola il valore secondo necessità e il codice che ha funzionato prima funziona ancora.

Ad esempio, supponiamo di scrivere una classe Point e decidiamo di passare dalla rappresentazione vettoriale (x, y) alla polar (angolo, grandezza) per memorizzare internamente la posizione del punto. Il codice che cerca un membro chiamato x è ora rotto - ma posso sempre fornire un metodo chiamato getX () che calcola la coordinata X del punto e lo restituisce.

    
risposta data 21.07.2014 - 01:47
fonte
7

Il primo problema che emerge in un progetto del genere è "cosa succede se qualcosa non è più definitivo?" Hai deciso che "no, hai davvero bisogno di cambiare quel campo a volte" e così quando rimuovi la finale, ti verrà lasciato un public int field; che rimarrà fuori per chiunque con cui giocherellare.

Il prossimo problema è che devi continuare a usarlo. Fa parte dell'API pubblica della classe. Se in seguito decidi che non ha alcun significato in alcune versioni future della classe refactored perché invece di definire un rettangolo come "lunghezza del lato corto, lunghezza del lato lungo" con quei due campi che sono pubblici, ora hai refactored così la rappresentazione interna è due Punto s in modo che tu possa usare più facilmente in AWT. Ma ci sono due campi che devi mantenere, anche se il codice interno non li usa più.

Questo diventa ancora più divertente quando estendi la classe. Perché ora tutti quelli che usano la classe estesa hanno anche quei due campi finali pubblici che loro devono mantenere ora e fanno parte della loro API.

Il problema dietro questi ultimi due esempi è che hai esposto alcune delle implementazioni della classe ad altre cose. Non è così male se i modificatori nei campi sono tali da essere limitati solo al codice che hai scritto. Una protezione predefinita (pacchetto) su un campo in modo che altre classi nel pacchetto possano accedervi direttamente? Non così male. Nessun getter in una classe interiore privata? Nessun problema.

Alcuni strumenti là fuori usano la riflessione e dipendono da getter e setter. Quello che più facilmente viene in mente è il linguaggio di espressione usato in JSP che si aspetta un oggetto compatibile con Java Bean. Se hai ${foo.total} richiama foo.getTotal() . Non appena il codice tocca JSP, diventa un po 'fastidioso per non avere getter e setter ( correlati SO ) - decoratori ovunque.

    
risposta data 21.07.2014 - 01:53
fonte
6

Vorrei aggiungere qualcosa che le persone non hanno menzionato:

Puoi scavalcare getter e setter e puoi specificarli nelle tue interfacce. Non puoi farlo con i campi.

    
risposta data 21.07.2014 - 12:55
fonte
5

Primo: è ragionevole usare le funzioni invece dei campi, perché la sintassi è diversa! Se hai codice che è int x = point.x e decidi di cambiare la rappresentazione interna di Point in un modello pigro che calcola x solo quando necessario, devi rifattorizzare tutto il codice legacy, dato che un accesso al campo pubblico e una chiamata al metodo sono fondamentalmente diverso: quindi il codice dovrebbe essere riscritto in int x = point.x()

Quindi perché dovremmo chiamare accessor-Methods ai campi getX () invece di solo X ()? perché è una convenzione. L'argomento perché tutti lo fanno è perfettamente accettabile qui. Perché scriviamo nomi di classe in Camel-Case? Perché prima dichiariamo Fields e poi Methods in una classe? Perché chiamiamo la variabile di iterazione in un semplice ciclo i?

Perché è idiomatico. Quasi tutti i programmatori Java se lo aspettano in questo modo e possono facilmente capire e mantenere il codice se segui queste convenzioni. Se scegli uno stile diverso e scegli nomi diversi e stile codice, dovresti avere un motivo valido perché questo altro stile è migliore, perché renderà il tuo codice meno portatile e più difficile da mantenere!

    
risposta data 21.07.2014 - 11:36
fonte
2

Non è sempre consigliato, leggi ad esempio le linee guida di Android:

Evita i getter / setter interni

Virtual method calls are expensive, much more so than instance field lookups. It's reasonable to follow common object-oriented programming practices and have getters and setters in the public interface, but within a class you should always access fields directly.

    
risposta data 21.07.2014 - 13:35
fonte
2

La risposta è che getter e setter offrono agli scrittori della biblioteca la flessibilità di modificare il funzionamento interno della loro libreria senza richiedere la ricompilazione degli utenti di tale libreria ogni volta che viene apportata una modifica. Questa area è chiamata compatibilità binaria o comportamentale ed è una delle basi dell'ingegneria del software e si è sviluppata perché l'esposizione degli interni di una classe può rapidamente diventare un incubo per la manutenzione.

È improbabile che ci sia un vantaggio in termini di prestazioni sia nell'accedere direttamente a un campo anziché tramite un getter, a meno che il compilatore HotSpot non sia al di sotto degli standard.

Ci sono alcuni casi, ad es. piccole classi, dove può essere considerato inutile.

Rinominare un campo finale e introdurre un getter è invece una pausa BC. Richiederà agli utenti della tua libreria di ricompilare che li farà incazzare (ho lavorato in situazioni in cui non ci è stato permesso di interrompere BC, o ci sarebbero state ramificazioni legali)

Per rendere tutto immutabile e fare una copia quando è necessario un cambiamento ... prova a ordinare un array con quella logica. L'immutabilità entra in gioco con la concorrenza quando i thread condividono i dati, ma c'è molto di più che rendere tutto immutabile.

La tua domanda è davvero un lamento sul perché Java non sia Scala, e la risposta è che è perché è Java.

    
risposta data 21.07.2014 - 16:31
fonte
1

Penso che sia principalmente per motivi legacy.

  • Alcune delle vecchie API di Java non sono immutabili (la classe Date è un oggetto mutevole, probabilmente c'è un'alternativa in java 8 ora). Quindi un argomento per l'utilizzo di getter è potenzialmente di restituire istanze immutabili di questi oggetti più vecchi. Questo è in contrasto con la programmazione funzionale in cui tutto è considerato immutabile di default.
  • Rendere le cose pubbliche e definitive non le rende necessariamente immutabili in java . Come semplice esempio se una lista fosse definitiva, l'oggetto potrebbe non essere mutabile ma i contenuti all'interno della lista sono (la programmazione funzionale restituirebbe una nuova lista immutabile in questo caso)
  • Java ha anche una specifica JavaBeans che raccomanda l'uso di getter e setter. Presumibilmente aiuta nella portabilità (che è probabilmente il motivo per cui le strutture usano questo per scopi di riflessione, sebbene alcuni ispezionino i campi stessi)
risposta data 21.07.2014 - 09:15
fonte
1

A volte qualcosa che si ottiene da un oggetto viene memorizzato internamente come campo, e talvolta non lo è. L'esempio che uso con i miei studenti è una classe Date . Sarebbe una follia rappresentare una data internamente come un giorno, un mese e un anno separati; usi semplicemente il numero di giorni trascorsi da qualche giorno di riferimento. Questo assicura coerenza e semplifica il confronto delle date e l'aritmetica.

Naturalmente, dal punto di vista dell'utente della tua classe, è una questione diversa. Vogliono sapere del mese e dell'anno e così via. Così li calcoliamo al volo e li esponiamo con getter.

Per me, questo è il vero cuore della programmazione: rendere le cose facili per i tuoi utenti e (se necessario) difficili per te . Questo significa lavorare dall'esterno in. Configurare l'API di cui l'utente ha bisogno e quindi preoccuparsi di come implementarla. E come al solito, se ti capita di essere il tuo utente, non cambia nulla. Ogni pezzo di codice ha un utente e uno sviluppatore.

Quindi immagina di essere un utente e vuoi chiedere all'oggetto foo il suo bar . Nel tuo mondo, devi smettere di considerare come viene implementato foo . bar è memorizzato esplicitamente come campo o è calcolato al volo?

Non si tratta solo di risparmiare tempo per l'utente, il fatto che un utente debba pensare ai dettagli di implementazione mostra un'astrazione seriamente lacerante. Come altri hanno sottolineato, ciò significa che non puoi semplicemente cambiare bar da un campo a un valore che è calcolato al volo. Dire che "devi ricompilare" non rende giustizia all'impatto. Devi rintracciare ogni pezzo di codice che chiama la tua classe e capire qual è l'impatto. Questo potrebbe essere in un'altra società, in un altro paese, e potrebbe essere un decennio più tardi.

Una buona codifica significa preoccuparsi delle interfacce dell'utente, perché nulla costa più della modifica di un'interfaccia. I getter sono un buon design dell'interfaccia.

    
risposta data 21.07.2014 - 21:27
fonte
0

Penso che una parte seria della risposta sia che gli sviluppatori Java sono abituati alla convenzione di usare getter e setter. Se si utilizza uno stile diverso, altri sviluppatori Java avranno un tempo leggermente più difficile leggere il codice. Considerando che la convenzione non costa nulla in termini di prestazioni o manutenibilità, credo che ne valga la leggibilità aumentata.

    
risposta data 21.07.2014 - 20:40
fonte
0

Anch'io sono un grande sostenitore di oggetti domini immutabili e ho visto casi in cui incapsulare il campo con un getter è sicuramente utile.

Un punto da notare è che l'immutabilità di un oggetto può essere piuttosto inutile se lo è solo in modo superficiale.

Per illustrare il punto, considera una classe che ha un campo finale (privato o meno) di tipo Data. Ora il punto di immutabilità è che il tuo oggetto non cambierà; potrebbe essere trasmesso ad altri metodi, memorizzato nella cache, condiviso su thread, ecc., senza timore di cambiare qualcosa.

Ma, considera se qualcuno riceve una sospensione di quel campo data. Non sto dicendo che Jimmy sia malizioso o stupido, ma può fare cose piuttosto sorprendenti, e non c'è alcuna garanzia che non chiamerà date.setTime su quel cucciolo.

Quindi, cosa fai? Usa l'incapsulamento. Rendi il campo privato e lo esponi tramite un accessor (getter), che invece di restituire l'oggetto dati memorizzato crea una copia con lo stesso valore. Oh, e tu fai la stessa cosa nel tuo costruttore (non vorrai che Jimmy mantenga il riferimento che ha fatto e faccia cose più dannose / stupide / sorprendenti, vero?)

Va bene, così ora esponi, diciamo, 7 campi direttamente e uno tramite un accessorio. E Jimmy arriva e vuole usare la tua API e, beh, che diavolo sta succedendo? Questo ragazzo mi sta dando i campi direttamente? O avvolgendoli nei metodi? Alcuni di ciascuno? Non possiamo essere coerenti, si chiede?

E in questo caso, sì, devo essere d'accordo con Jimmy.

    
risposta data 21.07.2014 - 20:53
fonte
0

L'uso degli accessori è conveniente nei seguenti casi:

  • Quando, durante alcuni deboli problemi, è necessario tenere traccia di chi accede a un determinato campo. Se esiste un metodo di accesso, è banale aggiungere il codice di monitoraggio pertinente in tale metodo; in caso contrario, questo è un gioco di ricerca più complesso e lungo tutto il codice che potenzialmente accede a quel campo (o potrebbe essere fatto con alcuni strumenti di magia, ma è difficile semplice ).

  • A un certo punto potresti voler risparmiare sulla memoria e sostituire il tuo campo "finale" con un pezzo di codice che ricalcola dinamicamente il valore, invece di tenerlo in giro. A seconda dei costi relativi della CPU e della RAM, questo può essere uno scambio utile - e poiché si tratta di un problema legato alle prestazioni, non può essere deciso fino a quando non saranno state prese misure effettive, cioè piuttosto tardi nel processo di sviluppo. A quel punto, la modifica di un metodo di accesso già esistente è molto più semplice rispetto al tracciamento di tutti i chiamanti su tutto il codice base.

Un punto importante è che Java era pensato per grandi progetti che coinvolgono molte persone; in tali contesti, non si può presumere che la ricompilazione dell'intero codice sia pratica o persino possibile. Le interfacce sono quelle in cui i team di sviluppatori si incontrano e sono molto difficili da modificare. La modifica di un accesso al campo in una chiamata al metodo è una modifica dell'interfaccia.

Sul lato delle prestazioni pure, Sun fa finta che il compilatore JIT sia abbastanza intelligente per le chiamate in linea a un metodo semplice, in modo che un metodo getter banale in realtà non costa nulla in fase di runtime. O così dicono.

Naturalmente, una convenzione è solo una convenzione. Le ragioni di cui sopra dovrebbero essere considerate come argomenti per l'utilizzo di accessors generalmente , ma questo non è un dogma. È più un promemoria che il ciclo di sviluppo del software è lungo, spesso coinvolge molte più persone di quanto inizialmente previsto, ei guadagni a breve termine devono essere bilanciati con i costi a lungo termine. Va bene usare i campi finali pubblici se questo va bene per te; ma ricorda che in qualsiasi progetto che coinvolge più di uno sviluppatore, non sei solo e le decisioni sull'interfaccia hanno un impatto su tutti.

    
risposta data 21.07.2014 - 22:32
fonte
0

Sono pienamente d'accordo con i dubbi e le conclusioni dell'autore di questa domanda.

IMMO in java getter sono principalmente usati perché persone come "regole del pollice" come "tutti i campi devono essere privati e hanno un getter" e in realtà non capisco perché è la vera ragione dietro a questo.

È davvero fastidioso quando vedo un "bean" java con N campi e getter & setter per tutti i campi, e le persone lo fanno perché "il suo oggetto orientato" quando in realtà questo rompe totalmente l'incapsulamento e crea un accoppiamento e più accoppiamento con un codice del genere:

  instance.getFoo().getBar().getFooBar()...

A volte non hai bisogno e non ti opponi, hai bisogno di una struttura dati, è perfettamente giusto mettere tutto il pubblico e le finali.

A volte vuoi un oggetto, se vuoi davvero un oggetto perché esponi lo stato interno con un getter ?. Ovviamente vuoi chiedere qualcosa al tuo oggetto (normalmente preferisco un tell non chiedere l'approccio, ma non sempre è possibile) chiedi all'oggetto quel valore, ma questo non è un getter, questo è un metodo che un oggetto esporre per dare alcune informazioni al mondo esterno, e in ogni modo questo implica che questo oggetto ha una proprietà che contiene questo valore!.

IMMO il termine "getter" è per me uno dei sintomi più noti del grande fraintendimento dell'orientamento agli oggetti nella comunità.

    
risposta data 21.07.2014 - 22:56
fonte

Leggi altre domande sui tag