Domanda

È completamente contrario al modo Java di creare oggetti simili a strutture?

class SomeData1 {
    public int x;
    public int y;
}

Posso vedere una classe con funzioni di accesso e mutatori più simili a Java.

class SomeData2 {
    int getX();
    void setX(int x);

    int getY();
    void setY(int y);

    private int x;
    private int y;
}

La classe del primo esempio è conveniente in termini di notazione.

// a function in a class
public int f(SomeData1 d) {
    return (3 * d.x) / d.y;
}

Questo non è così conveniente.

// a function in a class
public int f(SomeData2 d) {
    return (3 * d.getX()) / d.getY();
}
È stato utile?

Soluzione

Questo è un argomento comunemente discusso.Lo svantaggio della creazione di campi pubblici negli oggetti è che non si ha alcun controllo sui valori impostati su di essi.Nei progetti di gruppo in cui sono presenti molti programmatori che utilizzano lo stesso codice, è importante evitare effetti collaterali.Inoltre, a volte è meglio restituire una copia dell'oggetto del campo o trasformarlo in qualche modo, ecc.Puoi deridere tali metodi nei tuoi test.Se crei una nuova classe potresti non vedere tutte le azioni possibili.È come la programmazione difensiva: un giorno getter e setter potrebbero essere utili e non costa molto crearli/usarli.Quindi a volte sono utili.

In pratica, la maggior parte dei campi ha getter e setter semplici.Una possibile soluzione sarebbe simile a questa:

public property String foo;   
a->Foo = b->Foo;

Aggiornamento:È altamente improbabile che il supporto per le proprietà venga aggiunto in Java 7 o forse mai.Altri linguaggi JVM come Groovy, Scala, ecc. ora supportano questa funzionalità.-Alex Miller

Altri suggerimenti

Sembra che molte persone Java non abbiano familiarità con le linee guida di codifica Sun Java che affermano che è abbastanza appropriato utilizzare la variabile di istanza pubblica quando la classe è essenzialmente una "struttura", se Java supportava "struttura" (quando non c'è comportamento).

Le persone tendono a pensare che i getter e i setter siano il modo Java, come se fossero al centro di Java.Non è così.Se si seguono le linee guida di codifica Sun Java, utilizzando le variabili di istanza pubblica in situazioni appropriate, stai effettivamente scrivendo un codice migliore che ingombrandolo con getter e setter inutili.

Convenzioni del codice Java del 1999 e ancora invariato.

10.1 Fornire l'accesso alle variabili di istanza e classe

Non rendere pubblica alcuna istanza o variabile di classe senza una buona ragione.Spesso, non è necessario che le variabili di istanza siano impostate o ottenute esplicitamente, il che spesso avviene come effetto collaterale delle chiamate ai metodi.

Un esempio di variabili di istanza pubbliche appropriate è il caso in cui la classe è essenzialmente una struttura dati, senza comportamento. In altre parole, se avessi utilizzato una struttura invece di una classe (se Java supportava la struttura), allora è opportuno rendere pubbliche le variabili di istanza della classe.

http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-137265.html#177

http://en.wikipedia.org/wiki/Plain_old_data_structure

http://docs.oracle.com/javase/1.3/docs/guide/collections/designfaq.html#28

Usa davvero il buon senso.Se hai qualcosa come:

public class ScreenCoord2D{
    public int x;
    public int y;
}

Quindi non ha molto senso avvolgerli in getter e setter.Non memorizzerai mai una coordinata x, y in pixel interi in nessun altro modo.Getter e setter ti rallenteranno solo.

D'altra parte, con:

public class BankAccount{
    public int balance;
}

In futuro potresti voler modificare il modo in cui viene calcolato il saldo.Questo dovrebbe davvero usare getter e setter.

È sempre preferibile sapere Perché stai applicando le buone pratiche, in modo da sapere quando è giusto infrangere le regole.

Per risolvere i problemi di mutabilità puoi dichiarare xey come finali.Per esempio:

class Data {
  public final int x;
  public final int y;
  public Data( int x, int y){
    this.x = x;
    this.y = y;
  }
}

Il codice chiamante che tenta di scrivere su questi campi riceverà un errore in fase di compilazione del tipo "il campo x è dichiarato finale;non può essere assegnato".

Il codice cliente può quindi avere la comodità "abbreviata" che hai descritto nel tuo post

public class DataTest {
    public DataTest() {
        Data data1 = new Data(1, 5);
        Data data2 = new Data(2, 4);
        System.out.println(f(data1));
        System.out.println(f(data2));
    }

    public int f(Data d) {
        return (3 * d.x) / d.y;
    }

    public static void main(String[] args) {
        DataTest dataTest = new DataTest();
    }
}

Non usare public campi

Non utilizzare public campi quando vuoi davvero racchiudere il comportamento interno di una classe.Prendere java.io.BufferedReader Per esempio.Ha il seguente campo:

private boolean skipLF = false; // If the next character is a line feed, skip it

skipLF viene letto e scritto in tutti i metodi di lettura.Cosa succederebbe se una classe esterna in esecuzione in un thread separato modificasse in modo dannoso lo stato di skipLF nel bel mezzo di una lettura? BufferedReader andrà sicuramente in tilt.

Usalo public campi

Prendi questo Point classe ad esempio:

class Point {
    private double x;
    private double y;

    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public double getX() {
        return this.x;
    }

    public double getY() {
        return this.y;
    }

    public void setX(double x) {
        this.x = x;
    }

    public void setY(double y) {
        this.y = y;
    }
}

Ciò renderebbe molto doloroso scrivere il calcolo della distanza tra due punti.

Point a = new Point(5.0, 4.0);
Point b = new Point(4.0, 9.0);
double distance = Math.sqrt(Math.pow(b.getX() - a.getX(), 2) + Math.pow(b.getY() - a.getY(), 2));

La classe non ha alcun comportamento diverso dai semplici getter e setter.È accettabile utilizzare campi pubblici quando la classe rappresenta solo una struttura dati e non ha E non avrà mai un comportamento (getter e setter sottili lo sono non considerato il comportamento qui).Si può scrivere meglio così:

class Point {
    public double x;
    public double y;

    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }
}

Point a = new Point(5.0, 4.0);
Point b = new Point(4.0, 9.0);
double distance = Math.sqrt(Math.pow(b.x - a.x, 2) + Math.pow(b.y - a.y, 2));

Pulito!

Ma ricorda:Non solo la tua classe deve essere priva di comportamento, ma dovrebbe anche averla NO motivo per avere un comportamento anche in futuro.


(Questo è esattamente ciò questa risposta descrive.Per citare "Convenzioni del codice per il linguaggio di programmazione Java:10.Pratiche di programmazione":

Un esempio di variabili di istanza pubbliche appropriate è il caso in cui la classe è essenzialmente una struttura dati, senza comportamento.In altre parole, se avessi usato a struct invece di una classe (se Java supportato struct), allora è opportuno rendere pubbliche le variabili di istanza della classe.

Quindi anche la documentazione ufficiale accetta questa pratica.)


Inoltre, se sei estremamente sicuro che i membri di cui sopra Point dovrebbe essere immutabile, quindi potresti aggiungere final parola chiave per applicarlo:

public final double x;
public final double y;

A proposito, la struttura che stai fornendo come esempio esiste già nella libreria di classi base Java come java.awt.Point.Ha xey come campi pubblici, controllalo tu stesso.

Se sai cosa stai facendo e gli altri membri del tuo team lo sanno, allora va bene avere campi pubblici.Ma non dovresti fare affidamento su di esso perché possono causare mal di testa come nei bug relativi agli sviluppatori che utilizzano oggetti come se fossero strutture allocate nello stack (gli oggetti Java vengono sempre inviati ai metodi come riferimenti e non come copie).

Rif:aku, izb, John Topley...

Attenzione ai problemi di mutabilità...

Può sembrare sensato omettere getter/setter.In realtà potrebbe essere ok in alcuni casi.Il vero problema con il modello proposto qui mostrato è la mutabilità.

Il problema sorge quando si passa un riferimento a un oggetto contenente campi pubblici non finali.Qualsiasi altra cosa con quel riferimento è libera di modificare quei campi.Non hai più alcun controllo sullo stato di quell'oggetto.(Pensa a cosa accadrebbe se le stringhe fossero mutabili.)

Diventa brutto quando quell'oggetto è una parte importante dello stato interno di un altro, hai appena esposto l'implementazione interna.Per evitare ciò, è necessario restituire invece una copia dell'oggetto.Funziona, ma può causare un'enorme pressione sul GC a causa delle tonnellate di copie monouso create.

Se disponi di campi pubblici, valuta la possibilità di rendere la classe di sola lettura.Aggiungi i campi come parametri al costruttore e contrassegna i campi come definitivi.Altrimenti assicurati di non esporre lo stato interno e, se devi costruire nuove istanze per un valore restituito, assicurati che non venga chiamato eccessivamente.

Vedere:"Java efficace" di Joshua Bloch -- Articolo n. 13:Favorire l'immutabilità.

PS:Tieni inoltre presente che tutte le JVM al giorno d'oggi ottimizzeranno il getMethod, se possibile, risultando in una sola istruzione di lettura del campo.

L'ho provato in alcuni progetti, sulla base della teoria che getter e setter ingombrano il codice con sciocchezze semanticamente prive di significato e che altri linguaggi sembrano funzionare perfettamente con l'occultamento dei dati basato su convenzioni o la partizione delle responsabilità (ad es.pitone).

Come altri hanno notato sopra, ci sono 2 problemi in cui ti imbatti e non sono realmente risolvibili:

  • Quasi tutti gli strumenti automatizzati nel mondo Java si basano sulla convenzione getter/setter.Idem per, come notato da altri, tag jsp, configurazione Spring, strumenti Eclipse, ecc.eccetera...Combattere contro ciò che i tuoi strumenti si aspettano di vedere è una ricetta per lunghe sessioni di trolling su Google cercando di trovare quel modo non standard di avviare i fagioli primaverili.Davvero non ne vale la pena.
  • Una volta che hai la tua applicazione elegantemente codificata con centinaia di variabili pubbliche, probabilmente troverai almeno una situazione in cui sono insufficienti, dove hai assolutamente bisogno dell'immutabilità, o devi attivare qualche evento quando la variabile viene impostata, o vuoi lanciare un'eccezione su una modifica di variabile perché imposta lo stato di un oggetto su qualcosa di spiacevole.Sei quindi bloccato con le scelte non invidiabili tra ingombrare il tuo codice con qualche metodo speciale ovunque si faccia riferimento direttamente alla variabile, avere qualche modulo di accesso speciale per 3 delle 1000 variabili nella tua applicazione.

E questo è lo scenario migliore in cui si lavora interamente in un progetto privato autonomo.Una volta esportato il tutto in una libreria accessibile al pubblico, questi problemi diventeranno ancora più grandi.

Java è molto prolisso e questa è una cosa allettante da fare.Non farlo.

Se il metodo Java è il metodo OO, allora sì, la creazione di una classe con campi pubblici infrange i principi relativi all'occultamento delle informazioni che dicono che un oggetto dovrebbe gestire il proprio stato interno.(Quindi, dato che non sto solo parlando in gergo, un vantaggio dell'occultamento delle informazioni è che il funzionamento interno di una classe è nascosto dietro un'interfaccia - supponiamo che tu voglia cambiare il meccanismo con cui la tua classe struct ha salvato uno dei suoi campi, probabilmente dovrai tornare indietro e cambiare tutte le classi che utilizzano la classe...)

Inoltre, non puoi sfruttare il supporto per le classi conformi alla denominazione JavaBean, il che danneggerà se decidi, ad esempio, di utilizzare la classe in una pagina JavaServer scritta utilizzando Expression Language.

L'articolo di JavaWorld Perché i metodi Getter e Setter sono malvagi l'articolo potrebbe anche interessarti pensando a quando non implementare metodi di accesso e mutazione.

Se stai scrivendo una piccola soluzione e desideri ridurre al minimo la quantità di codice coinvolto, il metodo Java potrebbe non essere quello giusto: immagino che dipenda sempre da te e dal problema che stai cercando di risolvere.

Non c'è niente di sbagliato in quel tipo di codice, a condizione che l'autore conosce sono strutture (o navette dati) anziché oggetti.Molti sviluppatori Java non riescono a distinguere un oggetto ben formato (non solo una sottoclasse di java.lang.Object, ma un VERO oggetto in un dominio specifico) e un ananas.Ergo, finiscono per scrivere strutture quando hanno bisogno di oggetti e viceversa.

Il problema con l'utilizzo dell'accesso ai campi pubblici è lo stesso dell'utilizzo del metodo new invece di quello factory: se si cambia idea in seguito, tutti i chiamanti esistenti verranno interrotti.Quindi, dal punto di vista dell'evoluzione dell'API, di solito è una buona idea stringere i denti e utilizzare getter/setter.

Un posto in cui vado dall'altra parte è quando controlli fortemente l'accesso alla classe, ad esempio in una classe statica interna utilizzata come struttura dati interna.In questo caso, potrebbe essere molto più chiaro utilizzare l’accesso al campo.

A proposito, secondo l'affermazione di e-bartek, è altamente improbabile per l'IMO che il supporto per le proprietà venga aggiunto in Java 7.

Utilizzo spesso questo modello durante la creazione di classi interne private per semplificare il codice, ma non consiglierei di esporre tali oggetti in un'API pubblica.In generale, più frequentemente puoi rendere immutabili gli oggetti nella tua API pubblica, meglio è, e non è possibile costruire il tuo oggetto "simile a una struttura" in modo immutabile.

Per inciso, anche se scrivessi questo oggetto come una classe interna privata, fornirei comunque un costruttore per semplificare il codice per inizializzare l'oggetto.Dover avere 3 righe di codice per ottenere un oggetto utilizzabile quando ne basta uno è semplicemente complicato.

Una domanda molto, molto vecchia, ma permettetemi di dare un altro breve contributo.Java 8 ha introdotto espressioni lambda e riferimenti ai metodi.Le espressioni lambda possono essere semplici riferimenti a metodi e non dichiarare un corpo "vero".Ma non puoi "convertire" un campo in un riferimento al metodo.Così

stream.mapToInt(SomeData1::x)

non è legale, ma

stream.mapToInt(SomeData2::getX)

È.

Non vedo il danno se sai che sarà sempre una struttura semplice e che non vorrai mai associarvi un comportamento.

Questa è una domanda sulla progettazione orientata agli oggetti, non sul linguaggio Java.In genere è buona norma nascondere i tipi di dati all'interno della classe ed esporre solo i metodi che fanno parte dell'API della classe.Se esponi tipi di dati interni, non potrai mai modificarli in futuro.Se li nascondi, il tuo unico obbligo nei confronti dell'utente riguarda i tipi di ritorno e di argomento del metodo.

Qui creo un programma per inserire il nome e l'età di 5 persone diverse ed eseguire un ordinamento di selezione (in base all'età).Ho utilizzato una classe che funge da struttura (come il linguaggio di programmazione C) e una classe principale per eseguire l'operazione completa.Di seguito fornisco il codice...

import java.io.*;

class NameList {
    String name;
    int age;
}

class StructNameAge {
    public static void main(String [] args) throws IOException {

        NameList nl[]=new NameList[5]; // Create new radix of the structure NameList into 'nl' object
        NameList temp=new NameList(); // Create a temporary object of the structure

        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));

        /* Enter data into each radix of 'nl' object */

        for(int i=0; i<5; i++) {
            nl[i]=new NameList(); // Assign the structure into each radix

            System.out.print("Name: ");
            nl[i].name=br.readLine();

            System.out.print("Age: ");
            nl[i].age=Integer.parseInt(br.readLine());

            System.out.println();
        }

        /* Perform the sort (Selection Sort Method) */

        for(int i=0; i<4; i++) {
            for(int j=i+1; j<5; j++) {
                if(nl[i].age>nl[j].age) {
                    temp=nl[i];
                    nl[i]=nl[j];
                    nl[j]=temp;
                }
            }
        }

        /* Print each radix stored in 'nl' object */

        for(int i=0; i<5; i++)
            System.out.println(nl[i].name+" ("+nl[i].age+")");
    }
}

Il codice sopra è privo di errori e testato...Basta copiarlo e incollarlo nel tuo IDE e...Sai e cosa???:)

Puoi creare una classe semplice con campi pubblici e nessun metodo in Java, ma è pur sempre una classe e viene gestita sintatticamente e in termini di allocazione di memoria proprio come una classe.Non c'è modo di riprodurre veramente le strutture in Java.

A volte utilizzo tale classe, quando devo restituire più valori da un metodo.Naturalmente, tale oggetto ha vita breve e una visibilità molto limitata, quindi dovrebbe essere a posto.

Come per la maggior parte delle cose, c'è la regola generale e poi ci sono circostanze specifiche.Se stai realizzando un'applicazione chiusa e catturata in modo da sapere come verrà utilizzato un dato oggetto, puoi esercitare una maggiore libertà per favorire la visibilità e/o l'efficienza.Se stai sviluppando una classe che verrà utilizzata pubblicamente da altri al di fuori del tuo controllo, allora propendi verso il modello getter/setter.Come per tutte le cose basta usare il buon senso.Spesso va bene fare un giro iniziale con i pubblici e poi cambiarli in getter/setter in seguito.

La programmazione orientata agli aspetti consente di intercettare assegnazioni o recuperi e allegare loro la logica di intercettazione, che propongo sia il modo giusto per risolvere il problema.(La questione se debbano essere pubblici, protetti o protetti da pacchetti è ortogonale.)

Quindi inizi con campi non intercettati con il qualificatore di accesso corretto.Man mano che i requisiti del tuo programma crescono, alleghi la logica per forse convalidare, creare una copia dell'oggetto restituito, ecc.

La filosofia getter/setter impone costi su un gran numero di casi semplici in cui non sono necessari.

Il fatto che lo stile dell'aspetto sia più pulito o meno è in qualche modo qualitativo.Troverei facile vedere solo le variabili in una classe e visualizzare la logica separatamente.In effetti, la ragion d'essere della programmazione orientata ad Apect è che molte preoccupazioni sono trasversali e compartimentarle nel corpo della classe stessa non è l'ideale (il logging ne è un esempio - se vuoi registrare tutto ottiene Java vuole che tu lo faccia scrivi un sacco di getter e mantienili sincronizzati ma AspectJ ti consente una riga).

La questione dell'IDE è una falsa pista.Non è tanto la digitazione quanto la lettura e l'inquinamento visivo che deriva da get/sets.

A prima vista le annotazioni sembrano simili alla programmazione orientata agli aspetti, tuttavia richiedono di enumerare in modo esaustivo i punti di taglio allegando annotazioni, al contrario di una specifica concisa di punti di taglio simile a caratteri jolly in AspectJ.

Spero che la consapevolezza di AspectJ impedisca alle persone di stabilirsi prematuramente su linguaggi dinamici.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top