Domanda

Questa domanda ha già una risposta qui:

Per favore perdona la lunghezza, ma qui ci sono due programmi, entrambi esattamente uguali, ma uno con e uno senza setter, getter e costruttori.

Ho già frequentato un corso base di C++ e non ricordo nessuno di questi, e al momento non ne vedo il senso, se qualcuno potesse spiegarli in termini lamen, lo apprezzerei molto.. .al momento sembrano altro che perdite di spazio per far sembrare il mio codice più lungo, ma il professore dice che sono importanti (e per ora è tutto).

Grazie in anticipo!Ed ora ecco il codice:Chilometraggio.java:

package gasMileage;

import java.util.Scanner; //program uses class Scanner

public class Mileage 
{
    public int restart;
    public double miles, gallons, totalMiles, totalGallons, milesPerGallon;
    public Mileage(int newRestart, double newMiles, double newGallons, 
                   double newTotalMiles, double newTotalGallons, double newMilesPerGallon)
    {
        setRestart(newRestart);
        setMiles(newMiles);
        setGallons(newGallons);
        setTotalMiles(newTotalMiles);
        setTotalGallons(newTotalGallons);
        setMilesPerGallon(newMilesPerGallon);
    }
    public void setRestart(int newRestart)
    {
        restart = newRestart;
    }
    public int getRestart()
    {
        return restart;
    }
    public void setMiles(double newMiles)
    {
        miles = newMiles;
    }
    public double getMiles()
    {
        return miles;
    }
    public void setGallons(double newGallons)
    {
        gallons = newGallons;
    }
    public double getGallons()
    {
        return gallons;
    }
    public void setTotalMiles(double newTotalMiles)
    {
        totalMiles = newTotalMiles;
    }
    public double getTotalMiles()
    {
        return totalMiles;
    }
    public void setTotalGallons(double newTotalGallons)
    {
        totalGallons = newTotalGallons;
    }
    public double getTotalGallons()
    {
        return totalGallons;
    }
    public void setMilesPerGallon(double newMilesPerGallon)
    {
        milesPerGallon = newMilesPerGallon;
    }
    public double getMilesPerGallon()
    {
        return milesPerGallon;
    }
    public void calculateMileage()
    {
        Scanner input = new Scanner(System.in);
        while(restart == 1)
        {
            System.out.print("Please input number of miles you drove: ");
            miles = input.nextDouble();
            totalMiles = totalMiles + miles;
            System.out.print("Please input number of gallons you used: ");
            gallons = input.nextDouble();
            totalGallons = totalGallons + gallons;
            milesPerGallon = miles / gallons;
            System.out.printf("Your mileage is %.2f MPG.\n", milesPerGallon);
            System.out.print("Would you like to try again? 1 for yes, 2 for no: ");
            restart = input.nextInt();
        }
        milesPerGallon = totalMiles / totalGallons;
        System.out.printf("Your total mileage for these trips is: %.2f.\nYour total gas consumed on these trips was: %.2f.\n", totalMiles, totalGallons);
        System.out.printf("Your total mileage for these trips is: %.2f MPG", milesPerGallon);
    }
}

Mileagetest.java:

package gasMileage;

public class Mileagetest 
{
    public static void main(String[] args) 
    {
        Mileage myMileage = new Mileage(1,0,0,0,0,0);
        myMileage.calculateMileage();
    }
}

E ora per quello senza setter e getter:

Testmileage.java:

package gasMileage;

import java.util.Scanner;

public class Testmileage 
{
    int restart = 1;
    double miles = 0, milesTotal = 0, gas = 0, gasTotal = 0, mpg = 0;
    Scanner input = new Scanner(System.in);
    public void testCalculate()
    {
        while(restart == 1)
        {
            System.out.print("Please input miles: ");
            miles = input.nextDouble();
            milesTotal = milesTotal + miles;
            System.out.print("Please input gas: ");
            gas = input.nextDouble();
            gasTotal = gasTotal + gas;
            mpg = miles/gas;
            System.out.printf("MPG: %.2f", mpg);
            System.out.print("\nContinue? 1 = yes, 2 = no: ");
            restart = input.nextInt();
        }
            mpg = milesTotal / gasTotal;
            System.out.printf("Total Miles: %.2f\nTotal Gallons: %.2f\nTotal MPG: %.2f\n", milesTotal, gasTotal, mpg);
    }
}

Testmileagetest.java:

package gasMileage;

public class Testmileagetest 
{

    /**
     * @param args
     */
    public static void main(String[] args) 
    {
        Testmileage test = new Testmileage();
        test.testCalculate();
    }

}

Grazie ancora!

È stato utile?

Soluzione

Il punto di getter e setter, indipendentemente dalla lingua , è quello di nascondere la variabile sottostante. Ciò consente di aggiungere la logica verifica quando si tenta di impostare un valore - per esempio, se si ha un campo per una data di nascita, si potrebbe desiderare solo per permettere l'impostazione che campo per qualche tempo in passato. Questo non può essere eseguita se il campo è accessibile al pubblico e modifyable - è necessario il getter e setter

.

Anche se non hai bisogno di ancora alcuna verifica, potrebbe essere necessario in futuro. Scrivendo i getter e setter ora significa l'interfaccia è mantenuta costante, quindi il codice esistente non si rompe quando si modifica.

Altri suggerimenti

Le altre risposte generalmente danno una buona idea di alcuni motivi per l'utilizzo di getter e setter, ma voglio fare un esempio un po 'completo del perché sono utili.

Prendiamo, ad esempio, un file (ignorando l'esistenza di una classe File in Java). Questa classe File ha un campo per memorizzare il tipo di file (.pdf, exe, txt, ecc) ... ignoreremo tutto il resto.

Inizialmente si decide di salvarlo come String senza getter e setter:

public class File {
   // ...
   public String type;
   // ...
}

Qui ci sono alcuni problemi con non usare getter e setter.

Nessun controllo su come il campo è impostato:

Tutti i clienti della tua classe possono fare quello che vogliono con esso:

public void doSomething(File file) {
   // ...
   file.type = "this definitely isn't a normal file type";
   // ...
}

Si decide in seguito che probabilmente non si vuole loro di farlo ... ma dal momento che hanno accesso diretto al campo nella classe, non c'è modo di prevenirlo.

incapacità di cambiare facilmente rappresentazione interna:

Più tardi ancora, si decide che si desidera memorizzare il tipo di file come un'istanza di un FileType un'interfaccia chiamata, che consente di associare alcuni comportamenti con differenti tipi di file. Tuttavia, molti clienti della vostra classe sono già recuperando e impostando i tipi di file come Strings. Così si avrebbe un problema c'è ... che ci si rompe un sacco di codice (anche il codice in altri progetti che non si può risolvere da soli, se si tratta di una libreria) se solo cambiato il campo da un String ad un FileType .

Come getter e setter risolvere questo

Ora immaginate che aveva invece fatto il private tipo di campo e creato

public String getType() {
   return this.type;
}

public void setType(String type) {
   this.type = type;
}

Il controllo impostando la proprietà:

Ora, quando si desidera implementare un requisito che solo determinate stringhe sono tipi di file validi e prevenire altre stringhe, si può solo scrivere:

public void setType(String type) {
   if(!isValidType(type)) {
       throw new IllegalArgumentException("Invalid file type: " + type);
   }
   this.type = type;
}

private boolean isValidType(String type) {
   // logic here
}

Possibilità di cambiare facilmente rappresentazione interna:

La modifica della rappresentazione String del tipo è relativamente facile. Immaginate di avere un enum ValidFileType che implementa FileType e contiene i tipi validi di file.

Si potrebbe facilmente cambiare la rappresentazione interna del tipo di file nella classe come questa:

public class File {
   // ...
   private FileType type;
   // ...
   public String getType() {
      return type.toString();
   }

   public void setType(String type) {
      FileType newType = ValidFileType.valueOf(type);

      if(newType == null) {
         throw new IllegalArgumentException("Invalid file type: " + type);
      }

      this.type = newType;
   }
}

Dato che i clienti della classe hanno chiesto getType() e setType() in ogni caso, non cambia nulla dal loro punto di vista. Solo i interni della classe cambiato, non è l'interfaccia che le altre classi stanno utilizzando.

Encapsulation

metodi di accesso ( "setter e getter") tentativo di nascondere i dettagli su come i dati in un oggetto è stato memorizzato. In pratica, sono un mezzo glorificato per memorizzare e recuperare dati in modo non orientato agli oggetti. Di accesso non è incorporato in modo efficace nulla che c'è poca differenza pratica tra le seguenti due pezzi di codice:

Person bob = new Person();
Colour hair = bob.getHairColour();
hair.setRed( 255 );

E questo:

Person bob = new Person();
Colour hair = bob.hairColour;
hair.red = 255;

Entrambi i frammenti di codice esporre l'idea che una persona è strettamente legato ai capelli. Questo accoppiamento stretto allora si manifesta in tutta la base di codice, con conseguente software fragile. Cioè, diventa difficile da cambiare come i capelli di una persona viene memorizzato.

Invece:

Person bob = new Person();
bob.setHairColour( Colour.RED );

Questo segue la premessa di "dire, non chiedere". In altre parole, gli oggetti devono essere istruiti (da altri oggetti) per eseguire un compito specifico. Questo è il punto di programmazione orientata agli oggetti. E pochissime persone sembrano per farlo.

La differenza tra i due scenari è questo:

  • Nella prima situazione, Bob aveva alcun controllo su di che colore i suoi capelli sarebbe diventato. Ottimo per un parrucchiere con un debole per i capelli rossi, non così grande per Bob che disprezza quel colore.
  • Nel secondo caso, Bob ha il controllo completo su di che colore i suoi capelli diventeranno perché nessun altro oggetto del sistema è consentito di cambiare il colore che senza il permesso di Bob.

Un altro modo per evitare questo problema è quello di restituire una copia di colore dei capelli di Bob (come una nuova istanza), che non è più accoppiato a Bob. Trovo che per essere una soluzione poco elegante, perché significa che c'è un comportamento che altri desideri di classe, utilizzando i capelli di una persona, che non è più associato con la Persona stessa. Questo riduce la possibilità di riutilizzare il codice, che porta a codice duplicato.

Nascondere tipi di dati

In Java, che non può avere due firme di metodo che differiscono solo per il tipo di ritorno, in realtà non nasconde il tipo di dati sottostante utilizzato dall'oggetto. Avrete raramente, se mai, vedere il seguente:

public class Person {
  private long hColour = 1024;

  public Colour getHairColour() {
    return new Colour( hColour & 255, hColour << 8 & 255, hColour << 16 & 255 );
  }
}

Tipicamente, le singole variabili hanno il loro tipo di dati esposti integrale mediante la corrispondente metodo di accesso, e richiede refactoring per modificarlo:

public class Person {
  private long hColour = 1024;

  public long getHairColour() {
    return hColour;
  }

  /** Cannot exist in Java: compile error. */
  public Colour getHairColour() {
    return new Colour( hColour & 255, hColour << 8 & 255, hColour<< 16 & 255 );
  }
}

Mentre fornisce un livello di astrazione, è un velo sottile che non fa nulla per accoppiamento lasco.

Tell, Do not Ask

Per ulteriori informazioni su questo approccio, leggere Tell, Do not Ask .

File Esempio

Si consideri il seguente codice, leggermente modificato dalla risposta di Colind:

public class File {
   private String type = "";

   public String getType() {
      return this.type;
   }

   public void setType( String type ) {
      if( type = null ) {
        type = "";
      }

      this.type = type;
   }

   public boolean isValidType( String type ) {
      return getType().equalsIgnoreCase( type );
   }
}

Il metodo getType() in questo caso è ridondante e inevitabilmente (in pratica) portano a codice duplicato quali:

public void arbitraryMethod( File file ) {
  if( file.getType() == "JPEG" ) {
    // Code.
  }
}

public void anotherArbitraryMethod( File file ) {
  if( file.getType() == "WP" ) {
    // Code.
  }
}

Problemi:

  • Tipo dati. L'attributo type non può facilmente cambiare da una stringa in un numero intero (o un'altra classe).
  • Protocollo implicita. E 'tempo di astrarre il tipo dallo specifico (PNG, JPEG, TIFF, EPS) al generale (IMAGE, DOCUMENT, SPREADSHEET).
  • Introduce Bugs. Modifica del protocollo implicita non genererà un errore di compilazione, che può portare a bug.

Evitare completamente il problema impedendo altre classi da chiedendo per i dati:

public void arbitraryMethod( File file ) {
  if( file.isValidType( "JPEG" ) ) {
    // Code.
  }
}

Questo implica la modifica del metodo di accesso al get private:

public class File {
   public final static String TYPE_IMAGE = "IMAGE";

   private String type = "";

   private String getType() {
      return this.type;
   }

   public void setType( String type ) {
      if( type == null ) {
        type = "";
      }
      else if(
        type.equalsIgnoreCase( "JPEG" ) ||
        type.equalsIgnoreCase( "JPG" ) ||
        type.equalsIgnoreCase( "PNG" ) ) {
        type = File.TYPE_IMAGE;
      }

      this.type = type;
   }

   public boolean isValidType( String type ) {
      // Coerce the given type to a generic type.
      //
      File f = new File( this );
      f.setType( type );

      // Check if the generic type is valid.
      //
      return isValidGenericType( f.getType() );
   }
}

Nessun altro codice nel sistema si romperà quando la classe File transizioni protocollo implicita da tipi specifici (ad esempio, JPEG) ai tipi generici (ad es Image). Tutto il codice nel sistema deve utilizzare il metodo isValidType, che non dà il tipo all'oggetto chiamare, ma dice ilClasse File per convalidare un tipo.

L'idea è che se le classi client chiamano funzioni get / set, è possibile cambiare quello che fanno tardi e i chiamanti sono isolati. Se si dispone di una variabile pubblica, e io accedere direttamente, non c'è modo per aggiungere un comportamento più tardi, quando vi si accede o impostare.

Anche nel semplice esempio, si potrebbe prendere più vantaggio di esso.

Invece di usare:

milesPerGallon = miles / gallons;

in calculateMileage ()

Si potrebbe cambiare setMiles () e setGallons () per aggiornare milesPerGallon quando sono stati chiamati. Quindi, rimuovere setMilesPerGallon () per indicare che si tratta di una proprietà di sola lettura.

Il punto è che una classe non dovrebbe consentire l'accesso diretto ai suoi campi, perché questo è specifico dell'implementazione. Si consiglia di cambiare la classe più avanti, al fine di utilizzare un altro la memorizzazione dei dati, ma mantenere la classe lo stesso per i suoi "utenti", oppure si può decidere di creare un'interfaccia che non può includere i campi sia.

Date un'occhiata alla Wikipedia articolo sull'argomento.

Essi forniscono un'interfaccia pubblica per la classe, e un certo grado di incapsulamento. Pensate a come si potrebbe accedere ai dati pubblici senza getter e setter.

Mileage m = new Mileage();
m.miles = 5.0;
m.gallons = 10.0;
...

Ora, se si decide che si desidera aggiungere un po 'di convalida per la classe, è necessario modificare il codice in tutto il mondo che i campi erano direttamente accessibili. Se si basta usare getter e setter fin dall'inizio ( solo dove sono necessari ) si può evitare questo sforzo, e solo modificare il codice in un unico luogo.

Utilizzando getter e setter ti dà la flessibilità necessaria per modificare l'implementazione in seguito. Non si potrebbe pensare che è necessario, ma a volte si fa. Ad esempio, si potrebbe desiderare di utilizzare il modello di proxy a carico pigro un oggetto che è costoso da usare:

class ExpensiveObject {
    private int foo;

    public ExpensiveObject() {
       // Does something that takes a long time.
    }

    public int getFoo() { return foo; }
    public void setFoo(int i) { foo = i; }
}

class ExpensiveObjectProxy extends ExpensiveObject {
    private ExpensiveObject realObject;

    public ExpensiveObjectProxy() { ; }

    protected void Load() {
       if ( realObject == null ) realObject = new ExpensiveObject();
    }

    public int getFoo() { Load(); return realObject.getFoo(); }
    public void setFoo(int i) { Load(); realObject.setFoo(i); }
}

class Main {
    public static void main( string[] args ) {
         // This takes no time, since ExpensiveOjbect is not constructed yet.
         ExpensiveObject myObj = new ExpensiveObjectProxy();

         // ExpensiveObject is actually constructed here, when you first use it.
         int i = myObj.getFoo();
    }
}

Quando questo viene spesso a giocare è quando si hanno oggetti mappati ai database tramite un ORM. Hai solo caricare la roba che vi serve, per poi tornare al database per caricare il resto se / quando viene effettivamente utilizzato.

In generale, setter e getter sono stati un pessimo trucco da parte dei primi costruttori di GUI (Borland) per aggirare il fatto che tutte le variabili dovrebbero essere private (davvero, questo è assolutamente necessario)

Alcuni li chiamano astrazione, ma non lo sono.Un setter/getter standard non è migliore di un membro pubblico.Consentono comunque l'accesso completo alla variabile a volte che la classe non può controllare e limitano comunque le modifiche interne alla classe (se la tua variabile è un int, devi comunque cambiare tutto ciò che chiama setter e getter per cambiare la variabile in una stringa )

Getter e Setter incoraggiano l'accesso ai dati di una classe dall'esterno della classe.Qualsiasi codice che accede a un membro di una classe dovrebbe probabilmente esistere all'interno di quella classe (come afferma il tuo progetto) e quindi non dovrebbe aver bisogno di setter o getter.DOVREBBERO essere inutili.

Inoltre forzare un Setter in tutte le tue classi è orribile, significa che le tue classi semplicemente non possono essere immutabili mentre dovresti effettivamente avere una buona ragione per rendere una classe mutabile.

Detto questo, sono utili per questioni trasversali come i motori di persistenza e i costruttori di GUI in cui possono ottenere e impostare valori e la classe può monitorare ciò che è stato ottenuto o modificato e modificarlo o convalidarlo.

Un modello migliore per quei sistemi che necessitano dell'accesso trasversale alle variabili sarebbe quello di accedere alla variabile direttamente attraverso la riflessione MA chiamare un setter o getter se ne esiste uno, rendendo il setter e il getter privati ​​se possibile.

Ciò consentirebbe al codice trasversale non OO di funzionare correttamente, consentirebbe alla classe di modificare set e get quando necessario e consentire ai getter (che a volte sono davvero utili) dove necessario.

Il punto di metodi di accesso cioè. getter e setter è quello di fornire informazioni incapsulamento AKA nascondersi. E 'uno dei principi di base della programmazione orientata agli oggetti.

metodi di accesso

information hiding / incapsulamento

La risposta in una parola è interfacce .

Interfacce consentono di metodi, non i campi, in modo che il convenzione stabilita è quella di avere metodi GetX e setx per questo scopo.

(e interfacce è il modo per separare le funzionalità di implementazione in Java)

Il tuo esempio è estremo fino all'assurdità.Sì, tutti quei getter e setter gonfiano il codice e in quel caso non aggiungono alcun valore.Ma l’idea di fondo dell’incapsulamento è pensata per sistemi più grandi composti da molti componenti interagenti, non per programmi piccoli e autonomi.

Caratteristiche degli usi utili e sensati di getter e setter:

  • Una classe utilizzata da molte altre classi (nascondere i dettagli di implementazione rende il tutto più semplice per i client)
  • Getter e setter solo per i campi per i quali sono effettivamente necessari: il meno possibile, la maggior parte dei campi dovrebbe essere privata e utilizzata solo all'interno della propria classe
  • Pochissimi setter in generale:i campi mutabili rendono molto più difficile tenere traccia dello stato del programma rispetto ai campi di sola lettura
  • Getter e setter in realtà Fare qualcosa oltre all'accesso a un fied, ad es.setter che lanciano eccezioni per valori non validi o aggiornano un timestamp "ultima modifica" o un getter che calcola un valore al volo anziché fare affidamento su un campo sottostante

Fast forward di un paio di mesi. Forse il tuo insegnante ti chiede di implementare una versione remota della classe al chilometraggio. Forse come un servizio web, forse qualcos'altro.

Senza i getter / setter, che avrebbe dovuto cambiare ogni codice di tutto il mondo che acccesses un Chilometraggio, con i getter / setter è praticamente (in un mondo perfetto atleast) non resta che cambiare la creazione di un tipo di chilometraggio.

getter e setter consentono di creare scorciatoie utili per l'accesso e mutando i dati all'interno di un oggetto. Generalmente, questo può essere visto come alternativa ad avere due funzioni con un oggetto che vengono utilizzati per ottenere e impostare un valore, in questo modo:

{
    getValue: function(){
        return this._value;
    },
    setValue: function(val){
        this._value = val;
    }
}

L'ovvio vantaggio di scrivere JavaScript in questo modo è che si può utilizzare valori oscuri che non si desidera all'utente di accedere direttamente. Un qualcosa di risultato finale sembra il seguente (usando una chiusura per memorizzare il valore di un campo di nuova costruzione):

function Field(val){
    var value = val;

    this.getValue = function(){
        return value;
    };

    this.setValue = function(val){
        value = val;
    };
}

L'aggiunta di Setter e metodi getter Per rendere lo stato del bean gestito accessibili, è necessario aggiungere setter e getter metodi per quello stato. Il metodo createSalutation chiama il metodo bean'sgreet, e il metodo getSalutation recupera il risultato. Una volta aggiunti i metodi setter e getter, il fagiolo è completa. Il codice finale è simile al seguente: saluti pacchetto;

import javax.inject.Inject;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;

@Named
@RequestScoped
public class Printer {

    @Inject @Informal Greeting greeting;

    private String name;
    private String salutation;

    public void createSalutation() {
        this.salutation = greeting.greet(name);
    }

    public String getSalutation() {
        return salutation;
    }
    public String setName(String name) {
       this.name = name;
    }

    public String getName() {
       return name;
    }
}

Incapsulamento E capacità di riutilizzo del codice è la bellezza della programmazione orientata agli oggetti.Se abbiamo a che fare con alcuni dati sensibili nel nostro codice, li dichiariamo come campi di dati privati, ad es.incapsuliamo i nostri dati in modo che nessuno possa accedervi direttamente. Ora chiunque desideri accedere a tali campi dati deve utilizzare setter e getter, ad es.un meccanismo di accesso controllato per gestire i campi di dati sensibili.Il seguente esempio può essere utile per comprendere il vantaggio e l'importanza di setter e getter.

  • Ho implementato una classe in cui utilizzo la variabile days.
  • Nella mia classe nessuno può impostare un valore di giorni superiore a 365.
  • Qualcuno vuole ereditare dalla mia classe (riutilizzabilità del codice).
  • Ora, quando inserisce il valore dei giorni superiore a 365, tutte le funzionalità della mia classe falliranno.
  • Quindi avrei dovuto dichiarare la variabile days come campo dati privato.
  • Ora, se avessi dichiarato privato il campo dati dei giorni, nessuno avrebbe potuto impostare il valore dei giorni su più di 365 poiché avrei implementato una funzione setter con le limitazioni menzionate sull'input.
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top