Quando Getters e Setters sono giustificati

167

I getter e i setter sono spesso criticati come OO non appropriati. D'altra parte la maggior parte del codice OO che ho visto ha getter e setter estesi.

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

Se la tua lingua preferita ha delle proprietà (le mie), anche queste cose sono considerate getter e setter per questa domanda. Sono la stessa cosa da una prospettiva metodologica OO. Hanno solo una sintassi migliore.

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

Per esprimere semplicemente le critiche: Getter e Setter ti permettono 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 Versione procedurale del codice.

struct Fridge
{
    int cheese;
}

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

Versione del codice del mutatore:

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);        
}

I getter e i setter hanno reso il codice molto più complicato senza fornire un incapsulamento adeguato. Poiché lo stato interno è accessibile ad altri oggetti, non guadagniamo molto aggiungendo questi getter e setter.

La domanda è stata discussa in precedenza su Stack Overflow:

posta Winston Ewert 23.05.2017 - 14:40
fonte

16 risposte

157

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

Il punto di incapsulamento non è che non dovresti essere in grado di sapere o per cambiare lo stato dell'oggetto dall'esterno dell'oggetto, ma quello tu dovrebbe avere una politica ragionevole per farlo.

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

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

  • Alcuni membri dei dati potrebbero dover essere coerenti tra loro. Nel in questo caso non si fornisce un setter per ciascuno, ma un singolo metodo per impostarli allo stesso tempo, in modo da poter controllare valori per coerenza.

  • Alcuni membri dei dati possono solo essere modificati in un certo modo, ad esempio come incrementato o decrementato di un importo fisso. In questo caso, tu fornirebbe un metodo increment() e / o decrement() , piuttosto di un setter.

  • Eppure altri potrebbero effettivamente aver bisogno di essere letti e scrivere e avere entrambi un getter e un setter.

Considera un esempio di class Person . Diciamo che una persona ha un nome, un numero di previdenza sociale e un'età. Diciamo che non permettiamo alle persone di cambiare mai il loro nome o il numero di previdenza sociale. Tuttavia, l'età della persona dovrebbe essere incrementata di 1 ogni anno. In questo caso, fornirebbe un costruttore che inizializzerà il nome e l'SSN ai valori specificati e che inizializzerà l'età a 0. Fornirai anche un metodo incrementAge() , che aumenterebbe l'età di 1. Tu fornirebbe anche getter per tutti e tre. In questo caso non è richiesto alcun setter.

In questa progettazione si consente di controllare lo stato dell'oggetto dall'esterno della classe e di consentire che venga modificato dall'esterno della classe. Tuttavia, non si consente che lo stato venga modificato arbitrariamente. Esiste una politica che afferma in modo efficace che il nome e l'SSN non possono essere modificati affatto e che l'età può essere incrementata di 1 anno alla volta.

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

A proposito, nel tuo esempio, darei alla classe Fridge i metodi putCheese() e takeCheese() , invece di get_cheese() e set_cheese() . Allora 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() )
  }
}
    
risposta data 23.03.2018 - 00:37
fonte
41

La ragione di base per getter e setter in Java è molto semplice:

  • Puoi specificare metodi , non i campi, in un'interfaccia.

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

    
risposta data 26.11.2010 - 23:28
fonte
19

Da link

Stile JavaBean:

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

OO-style:

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

Bene, chiaramente preferisco il secondo approccio; non emette i dettagli di implementazione, è più semplice e conciso e tutte le informazioni necessarie sono incluse nella chiamata al metodo, quindi è più facile farlo correttamente. Preferisco anche impostare i membri privati utilizzando i 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 alcune informazioni.

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

Nel pensarci, quando ho iniziato a scrivere in C #, ho scritto un sacco di codice nello stile Javabeans illustrato sopra. Ma quando ho acquisito esperienza nel linguaggio, ho iniziato a fare più impostazioni dei membri nel costruttore e ad usare metodi che assomigliavano più allo stile OO sopra.

    
risposta data 27.11.2010 - 03:47
fonte
10

Come regola generale, getter e setter sono una cattiva idea. Se un campo non è logicamente parte dell'interfaccia e lo rendi privato, va bene. Se è logicamente parte dell'interfaccia e la rendi pubblica, va bene. Ma se lo rendi privato e poi ti giri e lo ritorni efficacemente in pubblico fornendo un getter e setter, torni al punto in cui hai iniziato tranne che il tuo codice ora è più prolisso e offuscato.

Ovviamente, ci sono delle eccezioni. In Java, potrebbe essere necessario utilizzare le interfacce. La libreria standard Java ha requisiti di compatibilità a ritroso 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 in seguito sostituire un campo memorizzato con un calcolo al volo senza interrompere l'interfaccia. Ma queste sono eccezioni. Getter e setter sono un anti-modello che ha bisogno di giustificazione speciale.

    
risposta data 25.08.2011 - 18:54
fonte
9

When are getters and setters justified?

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

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

Modifica

Questa risposta potrebbe sembrare 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 ciò che manca il quadro generale. Nella mia esperienza questo porta le persone a pensare che evitare i gettings e setters è una regola accademica per i linguaggi di programmazione OO (ad esempio, le persone pensano che la sostituzione dei getter con le proprietà sia grandiosa)

In effetti è una conseguenza del processo di progettazione. Non è necessario entrare nelle interfacce e nell'incapsulamento e nei modelli, discutendo sul fatto se sia o non rompa questi paradigmi e cosa sia o non sia una buona programmazione OO. L'unico punto che conta alla fine è che se non c'è nulla nel tuo dominio che funzioni in questo modo inserendoli non stai modellando il tuo dominio.

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

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

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

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

    
risposta data 08.09.2014 - 13:08
fonte
3

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

Gli invarianti di classe (utili) sono importanti. E per preservarli, a volte dobbiamo non essere in grado di cambiare qualcosa dall'esterno. Per esempio. se abbiamo un quadrato di classe con larghezza e altezza separate, cambiarne uno lo fa diventare diverso dal quadrato. Quindi abbiamo bisogno di cambiare metodo Se fosse Rettangolo, potremmo avere setter / campo pubblico. Ma setter di sarebbe testare se il suo maggiore di zero sarebbe meglio.

E nei linguaggi concreti (ad esempio java) sono le ragioni per cui abbiamo bisogno di questi metodi (interfacce). E un altro motivo per il metodo è la compatibilità (sorgente e binario). Quindi è più facile aggiungerli e poi pensare se il campo pubblico sia sufficiente.

btw. Mi piace usare classi di holding con valore immutabile semplice con campi finali pubblici.

    
risposta data 03.10.2011 - 15:17
fonte
2

Potresti voler cambiare i tuoi interni a qualsiasi cosa pur mantenendo le interfacce uguali. Se le tue interfacce non variano, il tuo codice non si romperà. Puoi ancora modificare i tuoi interni come desideri.

    
risposta data 26.08.2011 - 00:45
fonte
0

Il mio approccio è questo -

Quando mi aspetto di inserire i dati in un secondo momento, un getter / setter è giustificato. Inoltre, se il cambiamento sta accadendo, spingo spesso i dati in un getter / setter.

Se si tratta di una struttura POD, lascio gli slot disponibili.

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

    
risposta data 27.11.2010 - 00:08
fonte
0

Se usare getter e setter si sente complicato, il problema potrebbe essere il linguaggio, non il concetto stesso.

Ecco il codice dell'esempio secondo scritto in Ruby:

class Fridge
  attr_accessor :cheese
end

def go_shopping fridge
  fridge.cheese += 5
end

Si noti che somiglia molto all'esempio primo in Java? Quando getter e setter vengono trattati come cittadini di prima classe, non sono un compito da usare e la flessibilità aggiuntiva a volte può 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 le variabili interne inutilmente renderà il tuo codice peggiore, ma difficilmente puoi biasimarlo su getter e setter.

    
risposta data 04.10.2011 - 04:06
fonte
0

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

Gli oggetti sono costituiti da comportamenti e stati o attributi. Se non ci sono stati esposti, solo i comportamenti sono pubblici.

Senza stato, come classificheresti una collezione di oggetti? Come cercheresti un'istanza specifica di un oggetto? Se usi solo costruttori, cosa succede quando il tuo oggetto ha una lunga lista di attributi?

Nessun parametro del metodo dovrebbe mai essere consumato senza convalida. Quindi è pigrizia scrivere:

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

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

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

    
risposta data 16.07.2013 - 19:13
fonte
0

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

Ho sempre sentito che un oggetto rappresenta qualsiasi cosa ti venga in mente di averne bisogno.

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 faccio senza getter e setter? Java non usa le proprietà e l'impostazione di tutti i miei dati locali a questa classe in pubblico è DEFINITIVAMENTE una violazione di incapsulamento e OO.

    
risposta data 25.10.2016 - 20:30
fonte
-1

In primo luogo ci sono due tipi di oggetti che commenterò, valore e tipi di servizio.

I tipi di servizio non dovrebbero mai avere setter, qualunque sia la dipendenza di cui hanno bisogno non dovrebbe essere possibile. Il modo migliore per passare le dipendenze è tramite un costruttore o una fabbrica in questo modo tutte le istanze sono completamente formate dall'inizio, semplice e semplice.

I tipi di valore dovrebbero anche essere immutabili, sulla mano ci sono volte in cui questo non è pratico come un ORM, o qualche altra mappatura come da un widget ad un oggetto. Tutti gli altri tipi di valore che vengono passati al sistema da un livello o da una parte all'altra dovrebbero essere immutabili e non dovrebbero avere setter.

    
risposta data 03.10.2011 - 10:35
fonte
-1

Un Getter / Setter è giustificato come qualsiasi altro metodo pubblico è giustificato. La giustificazione è principalmente perché vogliamo fornire un'interfaccia al mondo esterno che definisca il modo in cui 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 corrente la stessa interfaccia di classe anche se nel frattempo quella classe ha aggiunto nuovi metodi (ma ha mantenuto la vecchia interfaccia)
  • Può modificare 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
risposta data 19.11.2011 - 13:43
fonte
-1

Sì, getter e setter è un anti-pattern nella programmazione orientata agli oggetti e dovrebbe mai essere utilizzato: link . In poche parole, non si adattano al paradigma orientato agli oggetti perché incoraggiano a trattare un oggetto come una struttura di dati. Quale è un malinteso importante.

    
risposta data 16.09.2014 - 12:56
fonte
-4

Mi auguro che tutto ciò che l'IDE sviluppato dalla società che sviluppa il tuo linguaggio di programmazione genera automaticamente non viola i "Principi di orientamento agli oggetti".

Detto questo, ogni volta che hai una variabile con scope pubblica, 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 proprio un sacco di codice junky.

Il motivo per usarli è che il corretto incapsulamento può ancora avere luogo e finché hai estratto il livello che permette alla persona che monkeying con il tuo oggetto di sentire il tuo oggetto, puoi portare via quell'oggetto senza che lui nemmeno sappia destra?

Basti dire che una casa divisa non regge, uno degli inquilini di OO non si accenderà da solo e non puoi servire sia l'incapsulamento che l'ottimizzazione prematura.

    
risposta data 27.11.2010 - 00:27
fonte
-4

Pensavo avremmo avuto più solite risposte qui?

I getter e i setter sono in generale estremamente OOP (chiunque affermi che qualcos'altro è sbagliato, semplice come quello).

Anche se è necessario ricordare di non sovrastare l'ingegnere, non creare mai getter o setter che non siano necessari. Qualsiasi informazione che non utilizzerai in un'altra classe non dovresti avere alcun getter o setter per.

Però, il motivo per cui non rendi pubblici i campi e invece hanno getter e setter sono principalmente:

  • Non è possibile eseguire test appropriati utilizzando i campi. I getter / setter sono molto meglio al riguardo.

  • È 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 così 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.

Quindi 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 puoi preoccuparti dei getter / setter.

    
risposta data 27.11.2010 - 19:15
fonte

Leggi altre domande sui tag