I metodi di una classe dovrebbero chiamare i suoi getter e setter?


53

Dove lavoro vedo molte classi che fanno cose come questa:

public class ClassThatCallsItsOwnGettersAndSetters {

    private String field;

    public String getField() {
        return field;
    }

    public void setField(String field) {
        this.field = field;
    }

    public void methodWithLogic() {
        setField("value");
        //do stuff
        String localField = getField();
        //do stuff with "localField"
    }
}

Se avessi scritto questo da zero, avrei scritto methodWithLogic()come questo invece:

public class ClassThatUsesItsOwnFields {

    private String field;

    public String getField() {
        return field;
    }

    public void setField(String field) {
        this.field = field;
    }

    public void methodWithLogic() {
        field = "value";
        //do stuff            
        //do stuff with "field"
    }
}

Sento che quando la classe chiama i suoi getter e setter, rende il codice più difficile da leggere. Per me quasi implica che in quella chiamata di metodo sta accadendo una logica complessa, anche se nel nostro caso non lo è quasi mai. Quando eseguo il debug di un codice sconosciuto, chi può dire che il bug non è un effetto collaterale in quel metodo? In altre parole, mi fa fare molti viaggi secondari nel viaggio di comprensione del codice.

Ci sono vantaggi per il primo metodo? Il primo metodo è effettivamente migliore?


Quando eseguo il debug di un codice sconosciuto, chi può dire che il bug non è un effetto collaterale in quel metodo? I tuoi test unitari. :)
Joshua Taylor,

1
L'uso di getter / setter nel tuo codice interno ti consente di creare un punto di interruzione nel tuo codice se lo attraversi con un debugger. Ti permette anche di assicurarti che se qualcosa non va nell'impostazione / acquisizione di un valore, sai che è a causa di quel metodo e non a causa di un accesso non valido. Nel complesso, fornisce più codice di coerenza.
Callyalater,

Risposte:


46

Non dirò quale sia il meglio o il peggio, perché dipende in parte dalla tua situazione (secondo me). Ma considera che i tuoi getter e setter potrebbero cambiare l'implementazione in un secondo momento, e bypassarli salterebbe quella logica.

Ad esempio, cosa succede se aggiungi un flag "sporco" ad alcuni campi setter in un secondo momento? Chiamando i tuoi setter nel tuo codice interno imposterai la bandiera sporca senza cambiare nessun altro codice. In molte situazioni questa sarebbe una buona cosa.


Ma che dire quando si inizializzano i dati nel back-end e non si desidera che vengano interpretati come sporchi. Se hai chiamato setField()dall'inizio hai appena introdotto un bug.
Daniel Kaplan,

6
Bene, ecco perché dico che dipende in parte dalla situazione. Forse l'inizializzazione dei dati dovrebbe saltare i setter, ma l'altro codice logico no. Scegli le regole appropriate e rispettale.
Matt S

3
@DanielKaplan: il punto è che chiamare getter e setter è nella maggior parte dei casi la cosa giusta da fare, e solo in situazioni specifiche no. Tuttavia, nel codice del mondo reale, quando si modifica un'implementazione getter / setter esistente in seguito per introdurre alcuni effetti collaterali intenzionali, molto probabilmente sarà necessario controllare ogni chiamata al getter o setter all'interno della classe e anche ogni accesso diretto al campo. Ecco perché dovresti cercare di mantenere le lezioni il più piccole possibile.
Doc Brown,

33

Chiamare direttamente il setter non è, di per sé, un problema. La domanda dovrebbe davvero essere: perché il nostro codice ha setter ovunque?

Le classi mutevoli sono un gioco pericoloso, in particolare dove la classe stessa gestisce il proprio stato. Considera quanti di questi setter hanno davvero bisogno di esistere. Quanti potrebbero essere impostati nel costruttore e quindi essere incapsulati interamente dalla classe stessa?

Se il campo deve essere configurabile esternamente, chiediti se il metodo con la logica dovrebbe essere presente. La tua stessa classe è davvero solo una classe data / state? Questa classe ha più di una responsabilità?

Non fraintendetemi, ci saranno casi in cui questo va bene. Non sto dicendo che dovresti eliminare completamente i setter sulle classi con la logica. Ma questi dovrebbero essere l'eccezione, non la regola. E, in quei pochi casi, dovrebbe essere ovvio a quale accedere direttamente.


1
Sono completamente d'accordo / pratico questa linea di pensiero. Penso anche che tu abbia sollevato un punto che è in effetti più importante della mia domanda. Detto questo, non mi sento di rispondere direttamente alla mia domanda originale. A meno che tu non stia dicendo "nel grande schema delle cose la tua domanda non ha importanza". Il che potrebbe anche essere vero :)
Daniel Kaplan il

@DanielKaplan: lo sto dicendo esattamente. :) Sono stato diviso tra risposta e commento con questo, ma sinceramente non sento che ci sia una risposta corretta che non fa la domanda più grande.
pdr

9

Sì, i metodi della tua classe dovrebbero chiamare getter e setter. Il punto cruciale nella scrittura di getter e setter è la correzione futura. È possibile rendere ogni proprietà un campo ed esporre direttamente i dati agli utenti della classe. Il motivo per cui si creano i getter e i setter non è necessariamente perché ora esiste una logica complessa, ma in tal modo l'interfaccia non si interromperà in futuro se è necessario aggiungerla.


1
Non vedo la relazione tra la tua prima frase e il resto del tuo paragrafo. La prima frase dice che una classe dovrebbe chiamare i propri getter e setter. Il resto del paragrafo spiega perché il codice client (es. Codice che utilizza la classe) dovrebbe usare i getter e i setter invece di accedere direttamente ai campi. Ma non vedo come questa sia la ragione per cui la classe non dovrebbe accedere direttamente ai propri campi.
Daniel Kaplan,

1
@DanielKaplan Il mio punto è che i motivi sono gli stessi. Che se aggiungi la logica setter in un secondo momento, ha un impatto sul codice interno (potenzialmente) altrettanto esterno.
Michael

2
@Michael: Spesso ci possono essere buoni motivi per cui una classe non usi i suoi getter / setter. Uno scenario comune è dove ci sono molti setter del modulo someField=newValue; refresh(); Se il metodo consente di impostare più campi, chiamare i setter per scrivere quei campi causerebbe operazioni di "aggiornamento" ridondanti. Scrivere tutti i campi e quindi chiamare refresh()una volta può produrre operazioni più efficienti e più fluide.
supercat

6

Per rispondere alle tue domande in una parola, sì.

Avere una classe che chiama i suoi getter e setter aggiunge estensibilità e fornisce una base migliore per il codice futuro.

Dì che hai qualcosa del genere:

public class Vehicle
{
    private int year;
    private String make;

    public Vehicle(int year, String make)
    {
        setYear(year);
        setMake(make);
    }

    public void setYear(int year)
    {
        this.year = year;
    }

    public void setMake(String make)
    {
        this.make = make;
    }
}

Chiamare i setter per anno e make attualmente non può aggiungere alcuna funzionalità, ma cosa succede se si desidera aggiungere qualcosa come la validazione dell'input ai setter?

public class Vehicle
{
    private int year;
    private String make;

    public Vehicle(int year, String make)
    {
        setYear(year);
        setMake(make);
    }

    public void setYear(int year)
    {
        if(year > 0)
        {
            this.year = year;
        }
        else
        {
            System.out.println(year + " is not a valid year!");
        }
    }

    public void setMake(String make)
    {
        this.make = make;
    }
}

5

Una cosa che non è stata menzionata è che getter e setter (come tutti i metodi) sono virtuali in Java. Ciò aggiunge un'altra funzionalità all'utilizzo costante nel codice. Un altro utente potrebbe estendere la tua classe, sovrascrivendo i tuoi getter e setter. La classe di base utilizzerà quindi i dati della sottoclasse anziché i propri. In una lingua in cui si contrassegnano esplicitamente le funzioni virtuali, questo è molto più utile poiché è possibile prevedere e dichiarare con quali funzioni è possibile eseguire. In Java, questo è qualcosa di cui dovresti sempre essere consapevole. Se si desidera il comportamento, usarli nel codice è una buona cosa, altrimenti non così tanto.


3

Se si sta tentando di realizzare qualcosa fornito dall'interfaccia pubblica, utilizzare i getter / setter.

Tuttavia, in quanto proprietario / sviluppatore della classe, puoi accedere alle sezioni private del tuo codice (dall'interno della classe ovviamente), ma sei anche responsabile della mitigazione dei pericoli.

Quindi forse hai un getter che scorre su un elenco interno e vuoi ottenere il valore corrente senza incrementare l'iteratore. In questo caso, utilizzare la variabile privata.

public class MyClass
{
    private int i;
    private List<string> list;
    public string getNextString()
    {
        i++;
        return list[i];
    }

    private void getString()
    {
        // Do not increment
        string currentString = list[i];

        // Increment
        string nextString = getNextString();
    }
}

Puoi dare il ragionamento per questo?
Daniel Kaplan,

1

Sì. Getter e setter rappresentano lo stato, quindi gira questa domanda: vuoi tenere traccia di più modi di cambiare lo stato di un oggetto nello stesso modo, a meno che non sia necessario?

Meno cose devi tenere traccia del meglio, ecco perché gli oggetti immutabili sono più facili da gestire.

Considera, non c'è nulla che ti impedisca di avere sia un campo pubblico che un getter / setter pubblico - ma cosa guadagneresti?

Occasionalmente può essere desiderabile o addirittura necessario accedere direttamente al campo per uno o più motivi, non dovresti evitarlo se ciò accade. Ma dovresti davvero farlo solo se c'è un vantaggio definito nel farlo.


Il punto della mia domanda è che sto solo chiedendo di accedere direttamente ai campi della stessa classe. Comprendo già lo scopo di getter e setter per il codice client.
Daniel Kaplan,

@DanielKaplan: lo capisco, e quello che sto dicendo è che, per quanto pratico, dovresti trattarli allo stesso modo.
jmoreno,

@DanielKaplan: Potresti avere un setter privato e un setter pubblico, che hanno lo stesso identico risultato (tutti gli effetti collaterali uguali) almeno per il momement, ma cosa ti guadagnerebbe se non la possibilità che le cose divergano? La differenza tra questo scenario e ciò che descrivi è che non puoi evitare di poter accedere allo stato al di fuori dei getter / setter, ma puoi evitare di farlo. Non complicare il codice a meno che non sia necessario.
jmoreno,

1

Entrambi i metodi hanno i loro casi d'uso. Poiché il setter pubblico mantiene coerente il valore del campo (e / o i valori associati), è necessario utilizzare setter quando la logica del metodo non interferisce con questa logica di coerenza. Se hai appena impostato la tua "proprietà", usa setter. D'altra parte, ci sono situazioni in cui è necessario l'accesso diretto ad alcuni campi, ad esempio operazioni in blocco con setter pesante o operazioni in cui il concetto di setter è troppo semplice.

È tua responsabilità mantenere le cose coerenti. Setter lo fa per definizione, ma non può coprire casi complessi.


1

Direi di no ..

Se i tuoi getter / setter ottengono e impostano (nessuna inizializzazione lenta, nessun controllo, ecc.) Allora usa solo le tue variabili. Se in futuro cambierai i tuoi getter / setter, puoi benissimo cambiare il tuo methodWithLogic()(perché sta cambiando la tua classe) e puoi chiamare un getter / setter invece di un incarico diretto. Puoi documentare questa chiamata per il getter / setter (poiché sarà strano chiamare un getter / setter quando il resto del tuo codice di classe utilizza direttamente la variabile).

La JVM inserirà le chiamate frequenti ai getter / setter. Quindi, non è mai un problema di prestazioni. Il vantaggio derivante dall'uso delle variabili è la leggibilità e l'IMHO, lo farei.

Spero che sia di aiuto..

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.