I getter e gli insedianti hanno un design scadente? Consiglio contraddittorio visto

Attualmente sto lavorando a un semplice gioco in Java con diverse modalità. Ho esteso una class Game principale per mettere la logica principale all’interno delle altre classi. Nonostante questo, la class di gioco principale è ancora piuttosto pesante.

Dopo aver dato una rapida occhiata al mio codice, la maggior parte di esso era Getters and Setters (60%) rispetto al resto che è veramente necessario per la logica del gioco.

Un paio di ricerche su Google hanno affermato che getter e setter sono malvagi, mentre altri hanno affermato che sono necessari per una buona pratica OO e grandi programmi.

Quindi cosa dovrei fare? Quale dovrebbe essere? Dovrei cambiare Getters e Setter per le mie variabili private o dovrei restare con loro?

C’è anche il punto di vista che la maggior parte delle volte, l’uso dei setter rompe ancora l’incapsulamento consentendo di impostare valori privi di significato. Come esempio molto ovvio, se hai un segnalino punteggio sul gioco che va sempre e solo, invece di

// Game private int score; public void setScore(int score) { this.score = score; } public int getScore() { return score; } // Usage game.setScore(game.getScore() + ENEMY_DESTROYED_SCORE); 

dovrebbe essere

 // Game private int score; public int getScore() { return score; } public void addScore(int delta) { score += delta; } // Usage game.addScore(ENEMY_DESTROYED_SCORE); 

Questo è forse un esempio un po ‘facile. Quello che sto cercando di dire è che discutere di getter / setter vs campi pubblici spesso offusca problemi più grandi con gli oggetti che manipolano lo stato interno di ciascuno in modo intimo e quindi sono troppo strettamente accoppiati.

L’idea è di creare metodi che facciano direttamente le cose che vuoi fare. Un esempio potrebbe essere come impostare lo stato “vivo” dei nemici. Potresti essere tentato di avere un metodo setAlive (booleano vivo). Invece dovresti avere:

 private boolean alive = true; public boolean isAlive() { return alive; } public void kill() { alive = false; } 

La ragione di ciò è che se cambi l’implementazione che le cose non hanno più un valore booleano “vivo” ma piuttosto un valore “punti ferita”, puoi cambiarlo senza rompere il contratto dei due metodi che hai scritto prima:

 private int hp; // Set in constructor. public boolean isAlive() { return hp > 0; } // Same method signature. public void kill() { hp = 0; } // Same method signature. public void damage(int damage) { hp -= damage; } 
  • Molto cattivo: campi pubblici.
  • Un po ‘malvagio: getter e setter dove non sono richiesti.
  • Bene: Getter e setter solo dove sono realmente richiesti – fanno in modo che il tipo mostri un comportamento “più grande” che capita di usare il suo stato, piuttosto che trattare il tipo come un repository di stato da manipolare con altri tipi.

Dipende molto dalla situazione – a volte vuoi davvero solo un object dati stupido.

Hai già avuto molte buone risposte su questo, quindi darò solo i miei due centesimi. Getter e setter sono molto, molto malvagi. In pratica, ti permettono di fingere di hide gli interni del tuo object quando la maggior parte delle volte tutto ciò che hai fatto è gettato in codice ridondante che non fa nulla per hide lo stato interno. Per un semplice POJO, non c’è motivo per cui getName () e setName () non possano essere sostituiti con obj.name = “Tom”.

Se la chiamata al metodo sostituisce semplicemente l’assegnazione, tutto ciò che hai ottenuto preferendo la chiamata al metodo è il codice gonfiato. Sfortunatamente, il linguaggio ha consacrato l’uso di getter e setter nella specifica JavaBeans, quindi i programmatori Java sono costretti a usarli, anche se ciò non ha alcun senso.

Fortunatamente, Eclipse (e probabilmente anche altri IDE) ti consente di generarli automaticamente. E per un progetto divertente, una volta ho costruito un generatore di codice per loro in XSLT. Ma se c’è una cosa di cui mi sbarazzerei in Java, è la sua eccessiva dipendenza da getter e setter.

Getter e setter applicano il concetto di incapsulamento nella programmazione orientata agli oggetti.

Avendo gli stati dell’object nascosti al mondo esterno, l’object è veramente responsabile di se stesso, e non può essere alterato in modi che non sono intesi. L’unico modo in cui l’object può essere manipolato è attraverso metodi pubblici esposti, come getter e setter.

Ci sono alcuni vantaggi per avere getter e setter:

1. Consentire modifiche future senza modifiche al codice che utilizza la class modificata.

Uno dei grandi vantaggi dell’utilizzo di un getter e setter è che una volta definiti i metodi pubblici e arriva un momento in cui è necessario modificare l’implementazione sottostante (ad esempio, trovare un bug che deve essere corretto, utilizzando un algoritmo diverso per migliorare le prestazioni , ecc.), poiché i getter e i setter sono l’unico modo per manipolare l’object, consentirà al codice esistente di non interrompersi e funzionerà come previsto anche dopo la modifica.

Ad esempio, supponiamo che esista un metodo setValue che imposta il value variabile privata in un object:

 public void setValue(int value) { this.value = value; } 

Ma poi, c’era un nuovo requisito che doveva tenere traccia del numero di volte che il value era cambiato. Con il setter in atto, il cambiamento è abbastanza banale:

 public void setValue(int value) { this.value = value; count++; } 

Se il campo del value era pubblico, non c’è un modo semplice per tornare più tardi e aggiungere un contatore che tenga traccia del numero di volte in cui il valore è stato modificato. Pertanto, avere getter e setter sono un modo per “rendere a prova di futuro” la class per i cambiamenti che potrebbero venire in seguito.

2. Applicare i mezzi attraverso i quali l’object può essere manipolato.

Un altro modo in cui i getter e i setter tornano utili è quello di far rispettare i modi in cui l’object può essere manipolato, quindi l’object ha il controllo del proprio stato. Con le variabili pubbliche di un object esposto, può essere facilmente danneggiato.

Ad esempio, un object ImmutableArray contiene un array int chiamato myArray . Se la matrice fosse un campo pubblico, non sarebbe immutabile:

 ImmutableArray a = new ImmutableArray(); int[] b = a.myArray; b[0] = 10; // Oops, the ImmutableArray a's contents have been changed. 

Per implementare un array veramente immutabile, è necessario scrivere un getter per l’array (metodo getArray ) in modo che restituisca una copia dell’array:

 public int[] getArray() { return myArray.clone(); } 

E anche se si verifica quanto segue:

 ImmutableArray a = new ImmutableArray(); int[] b = a.getArray(); b[0] = 10; // No problem, only the copy of the array is affected. 

ImmutableArray è davvero immutabile. Esporre le variabili di un object permetterà di manipolarlo in modi che non sono intesi, ma solo esponendo determinati modi (getter e setter), l’object può essere manipolato in modi intenzionali.

Suppongo che avere getter e setter siano più importanti per le classi che fanno parte di un’API che verrà utilizzata da altri, in quanto consente di mantenere intatta e invariata l’API pur consentendo modifiche nell’implementazione sottostante.

Con tutti i vantaggi di getter e setter, se il getter sta semplicemente restituendo il valore della variabile privata e il setter sta semplicemente accettando un valore e assegnandolo a una variabile privata, sembra che i getter e setter siano semplicemente estranei e davvero un rifiuto. Se la class sarà utilizzata solo per uso interno da un’applicazione che non verrà utilizzata da altri, l’uso di getter e setter in modo estensivo potrebbe non essere così importante come quando si scrive un’API pubblica.

Sono assolutamente cattivi.

@coobird sfortunatamente non “applicano il concetto di incapsulamento”, tutto ciò che fanno è farti pensare che stai incapsulando dei dati quando in realtà stai esponendo dati tramite una proprietà con delusioni di grandezza del metodo. Qualunque cosa che un getter / setter fa in un campo pubblico fa meglio.

In primo luogo, se si desidera che i dati pubblici siano resi pubblici, eliminare i metodi getter & setter per ridurre il numero di metodi che il client deve guadare e renderlo cognitivamente più semplice affinché il client modifichi il suo valore, ad es.

 object.field = value; 

invece del più cognitivamente intenso

 object.setField(value); 

dove il cliente deve ora controllare il metodo getter / setter per vedere se ha effetti collaterali.

In secondo luogo, se davvero hai bisogno di fare qualcos’altro nel metodo, perché chiamarlo metodo get / set quando ha più responsabilità che semplicemente ottenere o impostare? Segui l’SRP o chiama il metodo qualcosa che in realtà ti dice come funziona l’ intero metodo, come gli esempi di Zarkonnen che ha citato, ad esempio.

 public void kill(){ isAlive = false; removeFromWorld(this); } 

invece di

 public void setAlive(boolean isAlive){ this.isAlive = isAlive; if (isAlive) addToWorld(this); else removeFromWorld(this); } 

dove dice il metodo setAlive (booleano) al cliente che come effetto collaterale rimuoverà l’object dal mondo? Perché il cliente dovrebbe avere qualche conoscenza del campo isAlive? Inoltre cosa succede quando l’object viene nuovamente aggiunto al mondo, dovrebbe essere reinizializzato? perché il cliente dovrebbe preoccuparsi di tutto ciò?

IMHO la morale è di denominare metodi per dire esattamente quello che fanno, seguire l’SRP e sbarazzarsi di getter / setter. Se ci sono problemi senza getter / setter, dire agli oggetti di fare il proprio lavoro sporco all’interno della propria class invece di provare a fare cose con loro in altre classi.

qui finisce il mio rant, mi dispiace per quello;)

È un pendio scivoloso.

Un object di trasferimento semplice (o object Parametro) può avere l’unico scopo di contenere alcuni campi e fornire i loro valori su richiesta. Tuttavia, anche in quel caso degenerato si potrebbe sostenere che l’object dovrebbe essere immutabile – configurato nel costruttore e che espone solo i metodi get ….

C’è anche il caso di una class che espone alcune “manopole di controllo”; L’interfaccia utente della tua autoradio probabilmente può essere interpretata come getVolume , setVolume , getChannel e setChannel , ma la sua vera funzionalità è ricevere segnali ed emettere suoni. Ma quelle manopole non espongono molti dettagli di implementazione; non si sa da quelle caratteristiche dell’interfaccia se la radio è transistor, principalmente software o valvole a vuoto.

Più inizi a pensare a un object come partecipante attivo in un compito di dominio problematico, più penserai in termini di chiedere di fare qualcosa invece di chiedergli di dirti del suo stato interno, o di chiederlo i suoi dati così altri codici possono fare qualcosa con quei valori.

Quindi … “cattivo”? Non proprio. Ma ogni volta che sei incline a mettere un valore ed esporre entrambi get … e set … metodi su quel valore, chiediti perché e in che cosa consiste veramente la responsabilità di quell’object. Se l’unica risposta che puoi dare è “Per mantenere questo valore per me”, allora forse qualcosa oltre a OO sta succedendo qui.

La tua class di gioco probabilmente sta seguendo l’antipattern dell’object dio se espone così tante variabili. Non c’è niente di sbagliato nei getter e setter (anche se la loro verbosità in Java può essere un po ‘fastidiosa); in un’app ben progettata in cui ogni class ha una funzionalità chiaramente separata, non ne avrai bisogno di decine in una singola class.

Modifica: Se il punto principale per i getter e setter è di “configurare” la class di gioco (ho capito il tuo commento in questo modo), allora probabilmente non hai bisogno dei getter (è perfettamente per una class accedere alle sue variabili private senza usare i metodi get), e probabilmente è ansible far collassare molti dei setter in “group setter” che impostano più variabili che appartengono concettualmente.

La mia opinione è che getter e setter sono un requisito per i buoni programmi. Rimanere con loro, ma non scrivere getter / setter non necessari: non è sempre necessario gestire direttamente tutte le variabili.

La presenza di getter e setter tende ad indicare (un “odore” se si è in quel tipo di linguaggio della scuola primaria) che c’è un problema di progettazione. I getter e gli incastonatori banali sono appena distinguibili dai campi pubblici. In genere il codice che opera sui dati si troverà in una class diversa – incapsulamento scadente e cosa ci si aspetterebbe dai programmatori non a proprio agio con OO.

In alcuni casi getter e setter stanno bene. Ma di norma un tipo con getter e setter indica problemi di progettazione. I getter lavorano per l’immutabilità; i setter lavorano per “tell do not ask”. Sia l’immutabilità che “tell do not ask” sono buone scelte progettuali, purché non vengano applicate in uno stile sovrapposto.

Non penso davvero che siano cattivi. Ma mi piacerebbe vivere in un mondo in cui non ho mai dovuto usarli a meno che non ne avessi avuto bisogno.

Un esempio che ho letto sopra è stato il future-proofing codice. Per esempio:

 public void setValue(int value) { this.value = value; } 

Quindi, i requisiti cambiano ed è necessario tenere traccia di quante volte è stato impostato il valore.

Così:

 public void setValue(int value) { this.value = value; count++; } 

Questo è bellissimo. Capisco. Tuttavia, in Ruby, il seguente non avrebbe lo stesso scopo?

 someobject.my_value = 100 

Successivamente, è necessario tenere traccia del numero di volte in my_value stato impostato my_value . Bene, allora non potresti semplicemente ignorare il setter THEN e solo THEN ?

 def my_value=(value) @my_value = value @count++ end 

Sono tutto per codice bello, ma devo ammettere, guardando attraverso le montagne di classi Java che abbiamo e vedendo letteralmente migliaia e migliaia di linee di codice che NON SONO NULLA ma getter / setter di base è brutto e fastidioso.

Quando stavo sviluppando in C # a tempo pieno, usavamo proprietà pubbliche in ogni momento e facevamo getter / setter personalizzati solo quando necessario. Ha funzionato come un fascino e non ha infranto nulla.

Come sempre, l’unica risposta è: dipende. Se sei l’unica persona che tocca il codice, puoi fare tutto ciò che ti è comodo, incluso prendere scorciatoie.

Uno dei vantaggi dell’utilizzo di setter è che i controlli devono essere eseguiti in una sola posizione del codice.

Potresti voler prestare maggiore attenzione a ciò che viene effettivamente ottenuto e impostato con questi metodi. Se li stai utilizzando per fornire accesso a valori costanti, probabilmente stai meglio usando le costanti.

Questo dipende dal linguaggio di programmazione in questione. La tua domanda è inquadrata nel contesto di Java, dove sembra che getter e setter siano generalmente considerati come una buona cosa.

Al contrario, nel mondo Python, sono generalmente considerati come cattivi stili: aggiungono linee al codice senza aggiungere effettivamente funzionalità. Quando i programmatori Python devono, possono utilizzare la metaprogrammazione per ottenere l’acquisizione e / o l’impostazione degli attributi dell’object.

In Java (almeno la versione di Java che ho appreso poco più di un decennio fa), non era ansible. Pertanto, in Java è generalmente preferibile utilizzare getter e setter religiosamente, in modo che, se necessario, è ansible ignorare l’accesso alle variabili.

(Questo non rende Python necessariamente migliore di Java, solo diverso.)

Solo FYI: oltre a tutte le eccellenti risposte in questa discussione, ricorda che per tutti i motivi che puoi escogitare a favore o contro getter / setter, la performance non è una (come qualcuno potrebbe credere). La JVM è abbastanza intelligente da incorporare gocciolatori / setter banali (anche quelli non final , purché non siano effettivamente sovrascritti).

Potresti voler sostituire alcune classi per classi di valore. Ciò consentirà di rimuovere il getter ed evitare problemi quando il contenuto viene modificato da sotto di te.

Se hai bisogno di un accesso esterno ai singoli valori dei campi, usa getter e / o setter. Se no, non farlo. Non usare mai campi pubblici. E ‘così semplice! (Ok, non è mai così semplice, ma è una buona regola empirica).

In generale dovresti anche scoprire che devi fornire un setter molto meno spesso di un getter – specialmente se stai cercando di rendere i tuoi oggetti immutabili – che è una buona cosa (ma non sempre la scelta migliore) – ma anche se non lo sono.

Ho iniziato a programmare in Java per pochi mesi fa e ho imparato che dovremmo usare getter e setter solo quando è necessario per l’applicazione

divertiti 🙂