Quando sono giustificati Getter e setter


175

Getter e setter sono spesso criticati come non OO corretto. D'altra parte, la maggior parte del codice OO che ho visto ha getter e setter estesi.

Quando sono giustificati getter e setter? Cerchi di evitare di usarli? Sono abusati in generale?

Se la tua lingua preferita ha proprietà (la mia lo è), anche queste cose sono considerate getter e setter per questa domanda. Sono la stessa cosa dal punto di vista della metodologia OO. Hanno solo una sintassi migliore.

Fonti per la critica di Getter / Setter (alcune tratte dai commenti per dare loro una migliore visibilità):

Per esprimere semplicemente le critiche: Getter e setter consentono di manipolare lo stato interno degli oggetti dall'esterno dell'oggetto. Questo viola l'incapsulamento. Solo l'oggetto stesso dovrebbe preoccuparsi del suo stato interno.

E un esempio di versione procedurale del codice.

struct Fridge
{
    int cheese;
}

void go_shopping(Fridge fridge)
{
     fridge.cheese += 5;
}

Versione mutatore del codice:

class Fridge
{
     int cheese;

     void set_cheese(int _cheese) { cheese = _cheese; }
     int get_cheese() { return cheese; }
 }

void go_shopping(Fridge fridge)
{
     fridge.set_cheese(fridge.get_cheese() + 5);        
}

Getter e setter hanno reso il codice molto più complicato senza offrire l'incapsulamento corretto. Poiché lo stato interno è accessibile ad altri oggetti, non guadagniamo molto aggiungendo questi getter e setter.

La domanda è stata precedentemente discussa su Stack Overflow:


21
Getters and setters are often criticized as being not proper OO- Citazione, per favore.
Robert Harvey,


45
@Job, perché ha un codice? È una buona domanda e puzza di guerra santa se presa sul serio.
Peter Turner,

2
@Winston Ewert: "... la domanda è se dovresti accedere ai dati all'interno della classe.": Beh, nessuno ti costringe a implementare getter e setter per tutte le variabili membro, implementi solo quelle che ti servono. Ecco perché usi getter e setter invece di variabili membro pubbliche.
Giorgio,

2
@Winston Ewert: non vedo come l'assenza di getter / setter risolva questo problema: deve esserci un modo per accedere a ogni dato altrimenti è inutile; è compito di una buona progettazione decidere quale parte dei dati è accessibile a quale parte del codice. Se uno è un cattivo designer, lo sarà con o senza getter e setter. Solo i miei 2 centesimi.
Giorgio,

Risposte:


162

Avere getter e setter non rompe di per sé l'incapsulamento. Ciò che interrompe l'incapsulamento è l'aggiunta automatica di un getter e un setter per ogni membro di dati (ogni campo , in java lingo), senza pensarci. Sebbene sia meglio che rendere pubblici tutti i membri dei dati, è solo un piccolo passo.

Il punto di incapsulamento non è che non si dovrebbe essere in grado di conoscere o modificare lo stato dell'oggetto dall'esterno dell'oggetto, ma che si dovrebbe avere una politica ragionevole per farlo.

  • Alcuni membri dei dati potrebbero essere interamente interni all'oggetto e non dovrebbero avere né getter né setter.

  • Alcuni membri dei dati dovrebbero essere di sola lettura, quindi potrebbero aver bisogno di getter ma non di setter.

  • Alcuni membri dei dati potrebbero dover essere coerenti tra loro. In tal caso non forniresti un setter per ognuno, ma un singolo metodo per impostarli contemporaneamente, in modo da poter verificare la coerenza dei valori.

  • Alcuni membri dei dati potrebbero dover essere modificati solo in un determinato modo, ad esempio incrementati o decrementati di un importo fisso. In questo caso, forniresti un metodo increment()e / o decrement()piuttosto che un setter.

  • Tuttavia, altri potrebbero effettivamente aver bisogno di essere letti / scritti e avrebbero sia un getter che un setter.

Considera un esempio di a class Person. Supponiamo che una persona abbia un nome, un numero di previdenza sociale e un'età. Diciamo che non permettiamo alle persone di cambiare mai nome o numero di previdenza sociale. Tuttavia, l'età della persona dovrebbe essere aumentata di 1 ogni anno. In questo caso, forniresti un costruttore che inizializzerebbe il nome e l'SSN ai valori indicati e che inizializzerebbe l'età a 0. Forniresti anche un metodo incrementAge()che aumenterebbe l'età di 1. Forniresti anche Getter per tutti e tre. In questo caso non sono richiesti setter.

In questo progetto si consente di ispezionare lo stato dell'oggetto dall'esterno della classe e si consente di modificarlo dall'esterno della classe. Tuttavia, non si consente di modificare arbitrariamente lo stato. Esiste una politica che afferma efficacemente che il nome e il SSN non possono essere cambiati affatto e che l'età può essere aumentata di 1 anno alla volta.

Ora diciamo che una persona ha anche uno stipendio. E le persone possono cambiare lavoro a piacimento, il che significa che anche il loro stipendio cambierà. Per modellare questa situazione non abbiamo altro modo che fornire un setSalary()metodo! Consentire che lo stipendio venga modificato a piacimento è una politica perfettamente ragionevole in questo caso.

A proposito, nel tuo esempio, darei alla classe Fridgei metodi putCheese()e takeCheese(), invece di get_cheese()e set_cheese(). Quindi avresti ancora l'incapsulamento.


public class Fridge {
  private List objects;
  private Date warranty;

  /** How the warranty is stored internally is a detail. */
  public Fridge( Date warranty ) {
    // The Fridge can set its internal warranty, but it is not re-exposed.
    setWarranty( warranty );
  }

  /** Doesn't expose how the fridge knows it is empty. */
  public boolean isEmpty() {
    return getObjects().isEmpty();
  }

  /** When the fridge has no more room... */
  public boolean isFull() {
  }

  /** Answers whether the given object will fit. */
  public boolean canStore( Object o ) {
    boolean result = false;

    // Clients may not ask how much room remains in the fridge.
    if( o instanceof PhysicalObject ) {
      PhysicalObject po = (PhysicalObject)o;

      // How the fridge determines its remaining usable volume is a detail.
      // How a physical object determines whether it fits within a specified
      // volume is also a detail.
      result = po.isEnclosedBy( getUsableVolume() );
    }

     return result;
  }

  /** Doesn't expose how the fridge knows its warranty has expired. */
  public boolean isPastWarranty() {
    return getWarranty().before( new Date() );
  }

  /** Doesn't expose how objects are stored in the fridge. */
  public synchronized void store( Object o ) {
    validateExpiration( o );

    // Can the object fit?
    if( canStore( o ) ) {
      getObjects().add( o );
    }
    else {
      throw FridgeFullException( o );
    }
  }

  /** Doesn't expose how objects are removed from the fridge. */
  public synchronized void remove( Object o ) {
    if( !getObjects().contains( o ) ) {
      throw new ObjectNotFoundException( o );
    }

    getObjects().remove( o );

    validateExpiration( o );
  }

  /** Lazily initialized list, an implementation detail. */
  private synchronized List getObjects() {
    if( this.list == null ) { this.list = new List(); }
    return this.list;
  }

  /** How object expiration is determined is also a detail. */
  private void validateExpiration( Object o ) {
    // Objects can answer whether they have gone past a given
    // expiration date. How each object "knows" it has expired
    // is a detail. The Fridge might use a scanner and
    // items might have embedded RFID chips. It's a detail hidden
    // by proper encapsulation.
    if( o implements Expires && ((Expires)o).expiresBefore( today ) ) {
      throw new ExpiredObjectException( o );
    }
  }

  /** This creates a copy of the warranty for immutability purposes. */
  private void setWarranty( Date warranty ) {
    assert warranty != null;
    this.warranty = new Date( warranty.getTime() )
  }
}

4
Per favore, non prendere il mio esempio di frigorifero troppo sul serio. :) Un vero frigorifero dovrebbe essere un oggetto contenitore, mi aspetto che sappia come tenere gli oggetti senza preoccuparsi di cosa fossero esattamente. IE sarebbe come una ArrayList. Per quanto riguarda ciò che lo rende un frigorifero, diciamo che serializza gli oggetti su disco quando vengono archiviati in modo da sopravvivere a guasti del sistema. Ok. Basta prendere sul serio il mio esempio di frigo.
Winston Ewert,

6
Ma il frigorifero non dovrebbe conoscere le date di scadenza, in modo che possa dirti che il formaggio è andato male e dovrebbe essere buttato fuori? :) Ok, dobbiamo fermarlo! :)
Dima,

10
Piuttosto un'interessante discussione su Fridge Logic che hai avuto qui ...
Mason Wheeler,

35
Ahah, mi piacerebbe vedere l'annuncio per questo: "È un nuovissimo tipo di frigorifero: ti lancia cose quando cerchi di afferrare qualcosa che non c'è! In questo modo lo proverai solo una volta! Ti preoccupi riempiendo il frigorifero e lascia che il frigorifero si preoccupi di comportamenti illegali! "
gablin,

42
L'esempio ha sbagliato tutto. Non ci dovrebbe essere un campo Age né un metodo setAge (). L'età è una funzione della Data di nascita della persona rispetto a un certo momento. Sebbene apparentemente banale, questo è esattamente ciò che è sbagliato nella pratica moderna della piena mutabilità degli oggetti e di altri cattivi progetti, come visto dai metodi get / set per i campi privati ​​rispetto a considerare attentamente ciò che è realmente mutabile, che cos'è un campo, qual è l'oggetto dovrebbe conoscere i suoi comportamenti e quali cambiamenti di stato sono effettivamente validi (che i metodi setX distruggono completamente).
Darrell Teague,

41

Il motivo di base per getter e setter in Java è molto semplice:

  • È possibile specificare solo metodi , non campi, in un'interfaccia.

Pertanto, se si desidera consentire a un campo di passare attraverso l'interfaccia, è necessario un metodo di lettura e scrittura. Questi sono tradizionalmente chiamati getX e setX per il campo x.


44
Questa è una limitazione linguistica. La vera domanda è se dovremmo consentire ad altri oggetti di manipolare quello stato.
Winston Ewert,

3
@Peter Turner, chiaramente nulla impedisce a una lingua di avere interfacce che includono proprietà. Sotto le copertine che potrebbero essere implementate come getter / setter ma sarebbe abbastanza facile aggiungere supporto alle definizioni di interfaccia per le proprietà.
Winston Ewert,

2
@ Winston, alla fine dovrai consentire alle classi di scambiarsi informazioni tra loro per svolgere effettivamente il lavoro. Cosa suggeriresti invece?

6
Un oggetto dovrebbe fornire un'interfaccia di livello superiore alle sue viscere. Getter e setter tendono ad essere un'interfaccia di basso livello. Ad esempio, supponiamo di avere una classe che implementa un albero binario. Potresti avere funzioni come GetLeft (), GetRight (), GetValue () e GetKey () per navigare nella struttura. Ma questo è l'approccio completamente sbagliato. La tua classe di albero binario dovrebbe fornire operazioni come Trova, Inserisci, Elimina.
Winston Ewert,

6
Per fare un altro esempio, considera un pezzo di tetris. Il pezzo di tetris ha uno stato interno come block_type, rotazione, posizione. Potresti avere getter come GetBlockType (), GetRotation (), GetPosition (). Ma non dovresti davvero. Quello che dovresti avere è un metodo GetSquares () che restituisce un elenco di tutti i quadrati occupati da questo pezzo. Inoltre non dovresti avere cose come SetPosition () o SetRotation (). Invece, dovresti avere operazioni come MoveLeft (), MoveRight (), Rotate (), Fall ().
Winston Ewert,

20

Da http://www.adam-bien.com/roller/abien/entry/encapsulation_violation_with_getters_and

Stile JavaBean:

connection.setUser("dukie");
connection.setPwd("duke");
connection.initialize();

OO-style:

connection.connect("dukie","duke");

Bene, chiaramente preferisco il secondo approccio; non elimina i dettagli dell'implementazione, è più semplice e più conciso e tutte le informazioni necessarie sono incluse nella chiamata del metodo, quindi è più facile ottenerlo nel modo giusto. Preferisco anche impostare membri privati ​​usando parametri nel costruttore, quando possibile.

La tua domanda è: quando è giustificato un getter / setter? Forse quando è necessario un cambio di modalità o è necessario interrogare un oggetto per ottenere alcune informazioni.

myObject.GetStatus();
myObject.SomeCapabilitySwitch = true;

Pensandoci, quando ho iniziato a scrivere codice in C #, ho scritto molto codice nello stile giavanese illustrato sopra. Ma quando ho acquisito esperienza nella lingua, ho iniziato a creare più impostazioni dei membri nel costruttore e usando metodi che assomigliavano più allo stile OO sopra.


6
Perché lo stile OO "Dai tutti gli argomenti come parametri"?

5
Mentre il tuo "stile OO" va bene per 2 o 3 opzioni, preferirei lo stile JavaBean una volta superato. Avere 20 metodi sovraccarichi per ogni possibile combinazione di parametri sembra semplicemente terribile
TheLQ

1
@TheLQ: C # 4.0 rende tutto più semplice consentendo parametri opzionali e denominati nelle chiamate del costruttore e del metodo.
Robert Harvey,

7
@TheLQ Ecco a cosa serve Builder con sintassi fluida, vedi ad esempio MapMaker di Guava .
maaartinus,

5
@ Thorbjørn Ravn Andersen, direi "dare tutti gli argomenti come parametri" è meglio lo stile OO rispetto a "chiamare setter JavaBean" perché un setter implica che è impostato un attributo sottostante, che per definizione espone dettagli interni. L'uso del metodo connect (..) nell'esempio non fa ipotesi del genere; forse imposti gli attributi utente e password, forse no. L'importante è che ti concentri sul giusto concetto OO: il messaggio "connetti".
Andres F.

13

Quando sono giustificati getter e setter?

Quando il comportamento "get" e "set" corrispondono effettivamente al comportamento nel tuo modello, che non è praticamente mai .

Ogni altro uso è solo un imbroglio perché il comportamento del dominio aziendale non è chiaro.

modificare

Quella risposta potrebbe essere sembrata irriverente, quindi lasciami espandere. Le risposte di cui sopra sono per lo più corrette ma si concentrano sui paradigmi di programmazione del design OO e su alcuni che mancano il quadro generale. Nella mia esperienza, ciò porta le persone a pensare che evitare gettings e setter sia una regola accademica per i linguaggi di programmazione OO (ad esempio, le persone pensano che sostituire getter con proprietà sia grandioso)

In realtà è una conseguenza del processo di progettazione. Non devi entrare in interfacce, incapsulamenti e schemi, discutendo se rompa questi paradigmi e cosa sia o meno una buona programmazione OO. L'unico punto che alla fine conta è che se non c'è nulla nel tuo dominio che funzioni in questo modo inserendoli in te non stai modellando il tuo dominio.

La realtà è che è altamente improbabile che qualsiasi sistema nel tuo spazio di dominio abbia getter e setter. Non puoi avvicinarti all'uomo o alla donna che è responsabile del libro paga e dire semplicemente "Imposta questo stipendio su X" o "Ricevi questo stipendio" . Tale comportamento semplicemente non esiste

Se lo stai inserendo nel tuo codice, non stai progettando il tuo sistema in modo che corrisponda al modello del tuo dominio. Sì, questo rompe le interfacce e l'incapsulamento, ma non è questo il punto. Il punto è che stai modellando qualcosa che non esiste.

Più di te probabilmente ti manca un passaggio o un processo importante, perché c'è probabilmente un motivo per cui non posso semplicemente camminare per pagare il rotolo e dire di impostare questo stipendio su X.

Quando le persone usano getter e setter tendono a spingere le regole per questo processo nel posto sbagliato. Questo si sta allontanando ancora di più dal tuo dominio. Usando l'esempio del mondo reale, è come uno stipendio supponendo che la persona a caso che è entrata abbia il permesso di ottenere questi valori, altrimenti non li chiederebbe. Non solo non è così come è il dominio, ma in realtà sta mentendo su come è il dominio.


questo non sembra offrire nulla di sostanziale rispetto ai punti formulati e spiegati nelle risposte precedenti del 17
moscerino

1
Le altre risposte parlano di oggetti, interfacce e paradigmi di altri linguaggi. Va bene, ma non è il problema centrale. Getter e setter non sono solo cattivi perché infrangono alcune convenzioni di codifica, ma soprattutto perché non si incontra quasi mai questo comportamento nel modello reale che si sta rappresentando. Questo punto tende a perdersi nella discussione sulle migliori pratiche di codifica.
Cormac Mulhall,

Cosa pensi che sia l'interfaccia del libro paga se non è lo set/getstipendio?
Winston Ewert,

1
Se sono circondati da livelli di autorizzazione e limitati a determinate entità esterne, non sono getter e setter. Non sto dicendo che non esiste alcun metodo nel reparto buste paga che modifica lo stipendio, ma tale metodo dovrebbe allinearsi con i processi interni in atto nell'azienda stessa. Ad esempio, la maggior parte delle aziende fissa lo stipendio in base al contratto dei dipendenti, che è autorizzato da un manager. Se lo stipendio del dipendente cambia, viene redatto un nuovo contratto e un manager deve firmarlo. Pertanto il libro paga dovrebbe aspettarsi che un oggetto contratto e alcuni oggetti autorizzazione cambino il rotolo di paga
Cormac Mulhall,

3
O, per dirla in altro modo, c'è una bella differenza tra set_salary (new_salary, employee_id) e authorized_salary_update (employee_contract, authorising_manager). Il processo dovrebbe modellare il processo aziendale interno
Cormac Mulhall,

11

Come regola generale, getter e setter sono una cattiva idea. Se un campo non fa logicamente parte dell'interfaccia e lo rendi privato, va bene. Se fa logicamente parte dell'interfaccia e la rendi pubblica, va bene. Ma se lo rendi privato e poi ti giri e lo rendi effettivamente di nuovo pubblico fornendo un getter e setter, sei di nuovo al punto di partenza, tranne per il fatto che il tuo codice è ora più dettagliato e offuscato.

Ovviamente ci sono delle eccezioni. In Java, potrebbe essere necessario utilizzare le interfacce. La libreria standard Java ha requisiti di compatibilità con le versioni precedenti così estremi da superare le normali misure di qualità del codice. È anche possibile che tu abbia effettivamente a che fare con il leggendario ma raro caso in cui ci sono buone probabilità che tu possa successivamente sostituire un campo memorizzato con il calcolo al volo senza altrimenti interrompere l'interfaccia. Ma queste sono eccezioni. Getter e setter sono un anti-modello che richiede una giustificazione speciale.


6
Un getter e setter indicano che l'attributo, non il campo, fa parte dell'interfaccia. Supponiamo che getBirthDate () e SetBirthDate () prendano "yyyy / mm / dd" ma il campo memorizzato è il numero di giorni dal 01/01/1000. Puoi cambiarlo in 01/01/0001 e getter e setter manterranno l'interfaccia uguale. Soprattutto perché "pubblico" significa pubblicamente trasportabile, le variabili non dovrebbero mai essere pubbliche; utilizzare sempre un setter per convalidare il valore in entrata, aggiornare eventuali campi correlati, convertire in base alle esigenze, ecc. Provare a utilizzare un getter nel caso in cui la memoria interna cambi.
Andy Canfield,

@AndyCanfield è un po 'forte dire che pubblico significa pubblicamente trasportabile ... È così solo se lo programmi male. Se hai un setter e ti viene inviato un valore errato, potresti lanciare un'eccezione che potresti chiamare trashing del programma ma si sta chiudendo con un errore Inoltre, se l'utente dell'oggetto può dare un valore a una variabile e cud dargli l'errore quindi puoi tenerlo a mente e testarlo. Puoi dire "ah, ma devo fare solo 1 test se uso un setter", ma forse il tuo codice dà alla variabile un valore negativo ... quindi il setter potrebbe non riduci i tuoi test.n con i test, il tuo prog non viene "cestinato"
barlop

3

se il campo è accessibile direttamente o tramite il metodo non è davvero importante.

Gli invarianti di classe (quelli utili) sono importanti. E per preservarli, a volte abbiamo bisogno di non essere in grado di cambiare qualcosa dall'esterno. Per esempio. se abbiamo classe Square con larghezza e altezza separate, cambiando una di esse diventa qualcosa di diverso da piazza. Quindi abbiamo bisogno del metodo changeSide Se fosse Rectangle, potremmo avere setter / campo pubblico. Ma setter di che verificherebbe se il suo maggiore di zero sarebbe meglio.

E in linguaggi concreti (es. Java) ci sono ragioni per cui abbiamo bisogno di quei metodi (interfacce). E un altro motivo del metodo è la compatibilità (sorgente e binaria). Quindi è più facile aggiungerli e poi pensare se il campo pubblico sarebbe sufficiente.

btw. Mi piace usare semplici classi di valore immutabili con campi finali pubblici.


Questa è praticamente l'unica cosa che dovrebbe essere evitata. Passare dal campo alla proprietà / getter / setter è un cambiamento radicale nella maggior parte delle lingue.
MrDosu,

2

Potresti voler cambiare i tuoi interni in qualunque cosa mantenendo le stesse interfacce. Se le tue interfacce non variano, il codice non si romperà. Puoi ancora cambiare i tuoi interni come desideri.


1
Sono sorpreso che questa risposta sia così in basso. Getter e setter ti consentono di fare di più in futuro (ad es. Lanciare un evento, fare più convalida dell'input, fare contabilità interna) senza rompere l'API esistente. Anche con codice non pubblico, meno API devi modificare, meno dipendenze da aggiornare e meno bug introdotti durante gli aggiornamenti.
Amadeus Dr Zaius,

0

Il mio approccio è questo:

Quando mi aspetto di manipolare i dati in seguito, un getter / setter è giustificato. Inoltre, se si verificano cambiamenti, spesso inserisco i dati in un getter / setter.

Se è una struttura POD, lascio gli slot disponibili.

A un livello più astratto, la domanda è "chi gestisce i dati" e questo dipende dal progetto.


0

Se l'utilizzo di getter e setter sembra complicato, il problema potrebbe essere la lingua, non il concetto stesso.

Ecco il codice del secondo esempio scritto in Ruby:

class Fridge
  attr_accessor :cheese
end

def go_shopping fridge
  fridge.cheese += 5
end

Notate che assomiglia molto al primo esempio in Java? Quando getter e setter vengono trattati come cittadini di prima classe, non sono un lavoro ingrato da usare e la maggiore flessibilità può talvolta essere un vero vantaggio, ad esempio, potremmo decidere di restituire un valore predefinito per il formaggio su un nuovo frigorifero:

class Fridge
  attr_accessor :cheese

  def cheese
    @cheese || 0
  end
end

Naturalmente ci saranno molte variabili che non dovrebbero essere esposte pubblicamente. Esporre inutilmente le variabili interne peggiorerà il tuo codice, ma difficilmente puoi incolparlo su getter e setter.


Questo è magnificamente affermato. Le funzioni del costruttore sono state aggiunte perché vogliamo creare oggetti immutabili mentre gradualmente costruiamo la ricetta: forse se aggiungi dolcificante artificiale, non è necessario aggiungere zucchero. Utilizzando parametri nominati, e anche con sovraccarico, è molto difficile costruire un metodo facendo tutto. E poi ovviamente java non ha parametri nominati, quindi un motivo in più per cui le persone usano il modello Builder.
YoYo

0

Considera una Sizeclasse che incapsula larghezza e altezza. Posso eliminare i setter usando il costruttore ma come mi aiuta a disegnare un rettangolo Size? Larghezza e altezza non sono dati interni alla classe; sono dati condivisi che devono essere disponibili per gli utenti di Size.

Gli oggetti sono costituiti da comportamenti e stati - o attributi. Se non esiste uno stato esposto, solo i comportamenti sono pubblici.

Senza stato, come ordineresti una raccolta di oggetti? Come cercheresti un'istanza specifica di un oggetto? Se usi solo costruttori, cosa succede quando il tuo oggetto ha un lungo elenco di attributi?

Nessun parametro di metodo deve mai essere utilizzato senza convalida. Pertanto è pigrizia scrivere:

setMyField(int myField){
    this.myField = myField;
}

Se lo scrivi in ​​questo modo, ti sei almeno preparato per usare il setter per la validazione; è meglio di un campo pubblico - ma a malapena. Ma almeno hai un'interfaccia pubblica coerente che puoi tornare e inserire regole di convalida senza infrangere il codice dei tuoi clienti.

Getter, setter, proprietà, mutatori, chiamali come vuoi, ma sono necessari.


Ma almeno hai un'interfaccia pubblica coerente che puoi tornare e inserire regole di convalida senza infrangere il codice dei tuoi clienti. Non è vero. L'aggiunta di regole di convalida più avanti potrebbe "rompere il codice dei tuoi clienti". Prendi una intvariabile di istanza, ad esempio, e immagina di decidere di aggiungere una regola di validazione per accettare solo valori non negativi. Il problema è che il codice del tuo cliente potrebbe già fare affidamento sulla possibilità di impostarlo su un valore negativo ...
jub0bs

0

Se getter e setter violano l'incapsulamento e il vero OO, allora sono seriamente nei guai.

Ho sempre pensato che un oggetto rappresentasse qualunque cosa gli venga in mente che ti serve.

Ho appena finito di scrivere un programma che genera Mazes in Java e ho una classe che rappresenta "Maze Squares". Ho dati in questa classe che rappresentano coordinate, muri e booleani ecc.

Devo avere un modo per cambiare / manipolare / accedere a questi dati! Cosa devo fare senza getter e setter? Java non usa le proprietà e l'impostazione pubblica di tutti i miei dati locali di questa classe è Sicuramente una violazione di incapsulamento e OO.


6
Ecco la domanda: se rendessi pubblici tutti quei campi e smettessi di usare i getter e i setter come sarebbe diverso il tuo codice?
Winston Ewert,

In teoria, non lo è. Perché anche fare una lezione a quel punto? Lo stesso argomento potrebbe essere fatto con qualsiasi cosa. Ma il post sotto il mio sembra averlo detto più elegantemente. (Getter e setter sono un modo per passare in sicurezza un valore a un oggetto o recuperare un valore da quell'oggetto.) Il post continua ..
Bryan Harrington,

3
Vorrei sottolineare che quel poster alla fine è stato d'accordo con me e ha pubblicato un'altra risposta in tal senso. In sostanza, l'utilizzo di getter e setter equivale a manipolare direttamente i dati. In realtà non ottieni OOness. Per essere OO devi fornire un'interfaccia di livello superiore ai dati.
Winston Ewert,

4
Penso che il modo migliore per descrivere sarebbe dire che dovresti progettare le tue interfacce dall'interno verso l'esterno, non dall'interno verso l'esterno. L'interfaccia fornita dall'oggetto deve essere dettata dal modo in cui l'oggetto viene utilizzato e da come viene implementato. Quindi non scrivere getter e consentire a oggetti esterni di interpretare i dati. Scopri a quale domanda è necessario rispondere e scrivi un metodo che risponda a tale domanda. Non consentire agli oggetti esterni di modificare lo stato di un setter, scopri di che tipo di manipolazioni hanno bisogno per eseguire e scrivere i metodi che lo fanno.
Winston Ewert,

2
In alcuni casi, Getters e Setter sono il metodo migliore. A volte sono la migliore interfaccia che puoi fornire. Tuttavia, in molti casi non lo sono. In molti casi, stai perdendo i dettagli dell'implementazione in altre classi.
Winston Ewert,

-1

Innanzitutto ci sono due tipi di oggetti su cui commenterò, valore e tipi di servizio.

I tipi di servizio non dovrebbero mai avere setter, qualunque sia la loro dipendenza non dovrebbe essere ottenibile. Il modo migliore per passare le dipendenze è attraverso un costruttore o una fabbrica in questo modo tutte le istanze sono completamente formate dall'inizio, chiare e semplici.

I tipi di valore dovrebbero anche essere immutabili, d'altra parte ci sono momenti in cui ciò non è pratico come un ORM o qualche altra mappatura come da un widget a un oggetto. Tutti gli altri tipi di valore che passano intorno al sistema da un livello o parte a un altro dovrebbero essere immutabili e non dovrebbero avere setter.


Suggerirei un terzo tipo: un contenitore mutabile, il cui scopo è contenere uno o più valori; in molti casi, non ci si dovrebbe aspettare che un contenitore abbia molto in termini di logica o convalida. Il codice che necessita di un metodo per calcolare sei numeri interi positivi può passare un contenitore per sei numeri interi al metodo che memorizzerà i risultati al suo interno. La responsabilità di garantire che i numeri siano positivi dovrebbe generalmente ricadere sul chiamante o sul metodo chiamato, non sul contenitore.
supercat

-1

Un Getter / Setter è giustificato come qualsiasi altro metodo pubblico è giustificato. La giustificazione è principalmente perché vogliamo fornire un'interfaccia al mondo esterno che definisce come la nostra classe interagisce con altre classi. Lo facciamo principalmente perché vogliamo ridurre l'accoppiamento tra entità separate. Ridurre l'accoppiamento è una buona cosa per molte ragioni:

  • Posso usare dal mio codice attuale la stessa interfaccia di classe anche se nel frattempo quella classe ha aggiunto nuovi metodi (ma ha conservato la vecchia interfaccia)
  • Può cambiare la rappresentazione interna di una classe senza influire sui suoi consumatori
  • Riduci la possibilità di bug: se rendi privati ​​i tuoi dati interni puoi sapere con certezza che il codice esterno non interferirà con i tuoi dati interni

-1

Sì, getter e setter è un anti-pattern nella programmazione orientata agli oggetti e non dovrebbe mai essere usato: http://www.yegor256.com/2014/09/16/getters-and-setters-are-evil.html . In breve, non si adattano al paradigma orientato agli oggetti perché ti incoraggiano a trattare un oggetto come una struttura di dati. Qual è un grande malinteso.


2
questo non sembra offrire nulla di sostanziale rispetto ai punti formulati e spiegati nelle precedenti 18 risposte
moscerino del

4
È anche un po 'forte non dire mai .
Winston Ewert,

-4

Spero che tutto ciò che l'IDE sviluppato dall'azienda che sviluppa automaticamente il linguaggio di programmazione generato non violi i "Principi di orientamento degli oggetti".

Detto questo, ogni volta che hai una variabile con ambito pubblico, usa un getter e un setter e sei d'oro. Mi sembra essere l'elemento necessario del principio di incapsulamento estremamente orientato agli oggetti, anche se sembra solo un sacco di codice junky.

Il motivo per usarli è che l'incapsulamento corretto può ancora aver luogo e fintanto che hai sottratto lo strato che consente alla persona che monkeying con il tuo oggetto di sentire il tuo oggetto, puoi portarlo via senza che nemmeno lui sappia giusto?

Basti dire che una casa divisa non può sopportare, uno degli inquilini di OO non si accenderà da solo e non è possibile servire sia l'incapsulamento che l'ottimizzazione prematura.


4
e cosa ho guadagnato esattamente aggiungendo molto codice per chiamare setFoo () e getFoo () ogni volta che gestisco la variabile?
Winston Ewert,

7
Ecco la cosa, non penso che tu abbia l'incapsulamento. Stai ancora manipolando lo stato dell'oggetto da altri luoghi. Lo stai facendo in modo più verboso. Per ottenere l'incapsulamento, è necessario centralizzare la manipolazione dei valori nella classe che possiede quel valore. Nient'altro è solo un codice procedurale in abbigliamento OO. (Niente di necessariamente sbagliato in questo, ma è quello che è).
Winston Ewert,

2
Il vantaggio dell'incapsulamento è che tutta la logica relativa a valori particolari è in un posto (più o meno). Non capisco con getter e setter. Quindi, non penso di avere molti benefici per loro.
Winston Ewert,

1
Tuttavia, concorderò che preferirei avere un codice con getter e setter piuttosto che uno che manipoli i dati liberamente tramite l'accesso pubblico. In questo modo posso almeno allegare la convalida dei dati in arrivo.
Winston Ewert,

3
Non sono certo un fan della verbosità. Ma la mia obiezione è in realtà persone che rendono private le loro variabili e poi chiamano getter e setter su di loro finendo liberamente senza praticamente alcuna differenza. Se vuoi usare getter / setter all'interno della tua classe, va bene. La domanda è: perché? Il vantaggio principale di cui sono a conoscenza è che è possibile eseguire alcune convalide sui dati per rilevare errori in precedenza. Ciò è meno utile quando tutto il codice che manipola i dati si trova nello stesso posto. Ma può ancora essere utile. Idealmente, hai un linguaggio che supporta le proprietà e può evitare la verbosità.
Winston Ewert,

-4

Pensavo che avremmo avuto risposte più solite qui?

Getter e setter sono in genere estremamente OOP (chiunque dica qualcos'altro è sbagliato, così semplice).

Anche se devi ricordare di non esagerare con l'ingegnere, non fare mai un getter o setter che non è necessario. Qualsiasi informazione che non utilizzerai da un'altra classe per la quale non dovresti avere alcun getter o setter.

Tuttavia, il motivo per cui non rendi pubblici i campi e invece hai getter e setter sono principalmente:

  • Non è possibile effettuare test corretti utilizzando i campi. Getter / setter sono molto meglio in questo senso.

  • È impossibile utilizzare correttamente i campi in un'impostazione di programmazione in tempo reale. Getter / setter sincronizzati è la strada da percorrere.

  • L'argomento dell'interfaccia (già spiegato, quindi non lo farò).

  • È più facile da usare quando riutilizzi correttamente il sistema.

  • È molto più difficile sapere quali classi stanno usando la tua classe e cosa si romperà se cambi un campo di un getter / setter.

Pertanto l'unica volta che usi un campo pubblico invece di un getter / setter è quando non fai un progetto ufficiale ma invece lo fai solo per divertimento e non riesci a preoccuparti del getter / setter.


2
+1 per testabilità. Scioccato dal fatto che nessuno lo avesse menzionato prima. Un'altra utile funzionalità delle proprietà è il debug. (molto più facile inserire un punto di interruzione nel codice che richiedere punti di interruzione hardware / memoria per i campi).
Segna il

9
L'obiezione ai vincitori e ai setter è che sono spesso usati per seguire la lettera della legge OOP ignorando lo spirito e l'intento. OOP dice che non dovresti permettere ad altri oggetti di sporcare con i tuoi interni. Se definisci getter e setter che ti consentono di farlo comunque, finisci per violare OOP.
Winston Ewert,

6
Concordo sul fatto che ci sono molti vantaggi di getter e setter su semplici campi pubblici. Tuttavia, non sono chiaro come il test sia migliore con loro. Potresti spiegare?
Winston Ewert,

4
@Skarion: non sono d'accordo con la tua risposta. Sembra che tu stia presentando due alternative: usare getter / setter o rendere pubblici i campi. Ma c'è una terza opzione: non usare né getter / setter né esporre campi! Perché dovresti testare lo stato di un campo interno? Non dovresti testare l'API pubblica del tuo oggetto?
Andres F.
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.