Utilizzo di getter pubblici anziché privati


51

Vedo la maggior parte dei POJO immutabili scritti in questo modo:

public class MyObject {
    private final String foo;
    private final int bar;

    public MyObject(String foo, int bar) {
        this.foo = foo;
        this.bar = bar;
    }

    public String getFoo() {
        return foo;
    }

    public int getBar() {
        return bar;
    }
}

Eppure tendo a scriverli in questo modo:

public class MyObject {
    public final String foo;
    public final int bar;

    public MyObject(String foo, int bar) {
        this.foo = foo;
        this.bar = bar;
    }
}

Nota che i riferimenti sono definitivi, quindi l'oggetto è ancora immutabile. Mi permette di scrivere meno codice e consente un accesso più breve (di 5 caratteri: the gete ()).

L'unico svantaggio che posso vedere è se vuoi cambiare l'implementazione di getFoo()down the road per fare qualcosa di folle, non puoi. Ma realisticamente, ciò non accade mai perché l'Oggetto è immutabile; puoi verificare durante l'istanza, creare copie difensive immutabili durante l'istanza (vedi Guava's ImmutableListper esempio) e preparare gli oggetti fooo barper la getchiamata.

Ci sono degli svantaggi che mi mancano?

MODIFICARE

Suppongo che un altro svantaggio che mi manca siano le librerie di serializzazione che utilizzano la riflessione sui metodi che iniziano con geto is, ma è una pratica piuttosto terribile ...


Che diavolo? finalnon rende immutabile una variabile. Normalmente utilizzo un progetto in cui definisco i finalcampi prima di creare la richiamata in modo che la richiamata possa accedere a quei campi. Ovviamente può chiamare tutti i metodi compreso qualsiasi setXmetodo.
Tomáš Zato

@ TomášZato Non esistono metodi SETX su String, into MyObject. Nella prima versione, finalè solo per garantire che metodi diversi dal costruttore all'interno della classe non provino bar = 7;, ad esempio. Nella seconda versione, finalè necessario per evitare che i consumatori fare: MyObject x = new MyObject("hi", 5); x.bar = 7;.
Cory Kendall,

1
Bene, il tuo " Nota che i riferimenti sono definitivi, quindi Objectè ancora immutabile " è fuorviante - in questo modo sembra che pensi che final Objectsia immutabile, cosa che non lo è. Scusa per il fraintendimento.
Tomáš Zato

3
Se i valori sottostanti fossero mutabili, la strada privata + accessors non lo impedirebbe nemmeno - myObj.getFoo().setFrob(...).
Beni Cherniavsky-Paskin,

Risposte:


38

Quattro svantaggi a cui posso pensare:

  1. Se si desidera avere una forma di sola lettura e mutabile della stessa entità, un modello comune è quello di avere un'entità immutabile di classe che espone solo gli accessori con variabili membro protette, quindi creare un oggetto MutableEntity che lo estende e aggiunge setter. La tua versione lo impedisce.
  2. L'uso di getter e setter aderisce alla convenzione JavaBeans. Se si desidera utilizzare la classe come bean nelle tecnologie basate sulla proprietà, come JSTL o EL, è necessario esporre i getter pubblici.
  3. Se si desidera modificare l'implementazione per ricavare i valori o cercarli nel database, è necessario refactificare il codice client. Un approccio accessor / mutator consente di modificare solo l'implementazione.
  4. Il minimo stupore: quando vedo variabili di istanza pubblica, cerco immediatamente chi potrebbe averlo mutato e mi preoccupo di aprire la scatola di Pandora perché si perde l'incapsulamento. http://en.wikipedia.org/wiki/Principle_of_least_astonishment

Detto questo, la tua versione è decisamente più concisa. Se questa fosse una classe specializzata che viene utilizzata solo all'interno di un pacchetto specifico (forse l'ambito del pacchetto è una buona idea qui), allora potrei considerare questo per una tantum. Ma non vorrei esporre le principali API in questo modo.


3
Grandi risposte; Sono in disaccordo con alcuni di essi, ma non sono sicuro che questo sito sia il miglior forum di discussione. In breve, 1) posso spezzare gli altri usando la forma mutabile in cui assumono che sia immutabile (forse dovrei aggiungere la finale alla classe nei miei esempi), 2) Sono d'accordo, 3) Direi che non è più un POJO in questo caso, 4) Sono d'accordo per ora, ma spero che discussioni come questa possano cambiare ciò che è meno sorprendente :).
Cory Kendall,

2
5) Non è possibile esporre in modo sicuro classi / tipi intrinsecamente mutabili, come gli array. Il modo sicuro è quello di restituire una copia da un getter ogni volta.
Clockwork-Muse,

@ Clockwork-Muse puoi, se pensi che le persone non siano idioti. Quando accedi a someObj.thisList.add () è ovvio che stai modificando lo stato di alcuni altri oggetti. Con someObj.getThisList (). Add () non è noto. Devi chiedere la documentazione o cercare la fonte, ma dalla sola dichiarazione del metodo è impossibile dirlo.
kritzikratzi,

2
@kritzikratzi - Il problema è che non puoi garantire che il tuo codice non verrà eseguito in idioti. Ad un certo punto lo farà (che potrebbe anche finire per essere se stessi!), E la sorpresa potrebbe essere un grosso problema.
Clockwork-Muse,

1
Bene, lasciare che qualsiasi tipo mutabile erediti da un tipo immutabile è intrinsecamente sbagliato. Ricorda che un tipo immutabile offre la garanzia "Non cambio. Mai.".
Deduplicatore,

26

Sbarazzati anche dei getter / setter e stai bene!

Questo è un argomento molto controverso tra i programmatori Java.

Ad ogni modo, ci sono due situtation in cui uso variabili pubbliche anziché (!) Di getter / setter:

  1. finale pubblico Per me questo indica "Sono immutabile" molto meglio di un semplice getter. La maggior parte degli IDE indicherà il modificatore finale con una 'F' durante il completamento automatico. A differenza dei getter / setter, dove devi cercare l'assenza di un setXXX.
  2. pubblico non-finale Adoro questo per le classi di dati. Ho appena esposto tutti i campi pubblicamente. Nessun getter, setter, costruttori. No niente. Meno di un pojo. Per me questo segnala immediatamente "guarda, sono stupido. Conservo i dati, tutto qui. È TUO compito mettere i dati giusti dentro di me". GSON / JAXB / etc. gestire queste classi bene. Sono una felicità da scrivere. Non ci sono dubbi sul loro scopo o capacità. E, soprattutto: sai che non ci sono effetti collaterali quando cambi una variabile. IMHO si traduce in modelli di dati molto concisi con poche ambiguità, mentre getter e setter hanno questo enorme problema in cui a volte accade magia al loro interno.

5
È bello vedere un po 'di buonsenso di tanto in tanto :)
Navin,

2
Fino al giorno in cui il tuo modello si evolve e uno dei vecchi campi può ora essere derivato da un altro. Dal momento che si desidera mantenere la compatibilità con le versioni precedenti, il vecchio campo deve rimanere, ma invece di modificare semplicemente il codice getter e setter, è necessario modificare in qualsiasi altro luogo in cui questo vecchio campo è stato utilizzato per assicurarsi che segua la rappresentazione del nuovo modello. Facile da scrivere .. sì certo ... incubo da mantenere a lungo termine ... Ecco perché abbiamo getter / setter o meglio ancora negli accessi alle proprietà C #.
Newtopian,

9

Nelle parole di laici:

  • Si viola l'incapsulamento per salvare alcune righe di codice. Ciò sconfigge lo scopo di OOD.
  • Il codice del cliente sarà associato ai nomi dei membri della tua classe. L'accoppiamento è negativo. L'intero scopo di OOD è impedire l'accoppiamento.
  • Sei anche molto sicuro che la tua classe non dovrà mai essere mutevole. Le cose cambiano. Il cambiamento è l'unica cosa che è costante.

6
In che modo questa è una violazione dell'incapsulamento? Entrambe le versioni sono esposte al mondo esterno l'una all'altra (con accesso di sola lettura). Hai ragione sul duro accoppiamento e sull'inflessibilità del cambiamento, tuttavia, non lo discuterò.
KChaloux,

4
@KChaloux Confondi la "violazione dell'incapsulamento" con "il codice non riesce a compilare e devi perdere tempo a ripararlo", si verifica prima. Il secondo accade più tardi. Per chiarire: l'incapsulamento è già stato violato nel presente. Le interiora della tua classe non sono più incapsulate nel tempo presente. Ma il codice client si interromperà in futuro se la classe cambia in futuro, perché l'incapsulamento era rotto in passato. Esistono due diverse "interruzioni", l'incapsulamento oggi, il codice client domani, a causa della mancanza di incapsulamento.
Tulains Córdova,

8
Tecnicamente, quando usi getter / etc. il codice client verrà invece associato ai nomi dei metodi. Guarda quante librerie devono includere vecchie versioni di metodi con un tag / annotazione "deprecato" perché il codice più vecchio fa ancora affidamento su di esse (e alcune persone possono ancora usarle perché le trovano più facili da lavorare, ma non lo sei dovrebbe farlo).
JAB,

5
Pragmatico: con quale frequenza i programmatori rifattorizzano effettivamente i nomi dei campi privati ​​senza rinominare i getter / setter pubblici? Da quello che posso dire, esiste una convenzione a livello di comunità per nominarli uguali, anche se quella convenzione può essere solo il risultato di strumenti IDE che generano getter e setter dai nomi dei campi. Teorico: dal punto di vista della modellazione orientata agli oggetti, tuttavia, tutto ciò che è pubblico fa parte del contratto pubblico e non un dettaglio di implementazione interno. Quindi, se qualcuno decide di rendere pubblico un campo finale, non sta dicendo che questo è il contratto che promettono di adempiere?
Thomas Jung,

3
@ user949300 Dimmi quali possono essere appresi.
Tulains Córdova,

6

Un possibile svantaggio che riesco a vedere con disinvoltura è che sei legato alla rappresentazione interna dei dati nella classe. Questo probabilmente non è un grosso problema, ma se usi i setter e decidi che foo and bar verranno restituiti da qualche altra classe definita altrove, le classi che consumano MyObject non dovrebbero cambiare. Dovresti solo toccare MyObject. Tuttavia, se usi i valori nudi, dovresti toccare ovunque il tuo MyObject che ho usato.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.