Question

    

Cette question a déjà une réponse ici:

         

S'il vous plaît pardonnez la longueur, mais voici deux programmes, à la fois exactement le même, mais avec et un sans setters, getters et constructeurs.

J'ai pris une classe C ++ de base avant et ne me souviens de ces de lui, et pour le moment je ne vois pas le point d'eux, si quelqu'un pouvait les expliquer en termes Lamen je serais reconnaissant de bien vouloir il ... au moment où ils semblent être rien de plus que gaspilleurs d'espace pour faire mon code regarder plus, mais l'enseignant dit qu'ils sont importants (et jusqu'à présent, c'est tout).

Merci d'avance! Et maintenant, voici le code: Mileage.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();
    }
}

Et maintenant l'un sans setters et getters:

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

}

Merci encore!

Était-ce utile?

La solution

Le point de accesseurs, quelle que soit la langue , est de cacher la variable sous-jacente. Cela vous permet d'ajouter une logique de vérification lors d'une tentative de définir une valeur - par exemple, si vous aviez un champ pour une date de naissance, vous voudrez peut-être seulement pour permettre la mise en ce domaine à un moment donné dans le passé. Cela ne peut pas être appliquée si le champ est accessible au public et modifyable - vous avez besoin des accesseurs

.

Même si vous ne avez pas besoin de vérification encore, vous pourriez avoir besoin dans l'avenir. L'écriture des accesseurs signifie maintenant l'interface est maintenue constante, de sorte que le code existant ne se cassera pas quand vous le changiez.

Autres conseils

Les autres réponses donnent généralement une bonne idée de quelques raisons d'utiliser des accesseurs, mais je veux donner un exemple un peu complet des raisons pour lesquelles ils sont utiles.

Prenons, par exemple, un fichier (en ignorant l'existence d'une classe File en Java). Cette classe File a un champ pour stocker le type du fichier (.pdf, .exe, txt, etc) ... nous ignorerons tout le reste.

Au départ, vous décidez de l'enregistrer comme String sans accesseurs:

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

Voici quelques questions à ne pas utiliser accesseurs.

Aucun contrôle sur la façon dont le champ est défini:

Tous les clients de votre classe peuvent faire ce qu'ils veulent avec elle:

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

Vous décidez que vous ne voulez probablement pas à le faire ... mais comme ils ont un accès direct au champ dans votre classe, vous avez aucun moyen de l'empêcher.

Impossibilité de changer facilement représentation interne:

Plus tard, vous décidez que vous voulez enregistrer le type de fichier comme une instance d'une interface appelée FileType, vous permettant d'associer un comportement à différents types de fichiers. Cependant, de nombreux clients de votre classe sont déjà en train de récupérer et mettre les types de fichiers Strings. Donc, vous auriez un problème là ... vous brisaient beaucoup de code (même code dans d'autres projets que vous ne pouvez pas vous fixer, si elle est une bibliothèque) si vous venez de modifier le champ d'un String à un FileType .

Comment accesseurs et de mutateurs résoudre ce

Maintenant, imaginez que vous aviez fait à la place du champ de type private et créé

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

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

Le contrôle définissant la propriété:

Maintenant, quand vous voulez mettre en œuvre une exigence que seules certaines chaînes sont des types de fichiers valides et empêcher que d'autres chaînes, vous pouvez simplement écrire:

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é de changer facilement représentation interne:

Modification de la représentation String du type est relativement facile. Imaginez que vous avez un enum de ValidFileType qui implémente FileType et contient les types valides de fichiers.

Vous pouvez facilement changer la représentation interne du type de fichier dans la classe comme ceci:

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

Étant donné que les clients de la classe ont appelé getType() et setType() de toute façon, rien ne change de leur point de vue. Seuls les entrailles de la classe a changé, pas l'interface que les autres classes utilisent.

Encapsulation

Méthodes accesseurs ( « setters et getters ») tentent de cacher les détails sur la façon dont les données dans un objet sont stockées. Dans la pratique, ils sont un moyen glorifié pour stocker et récupérer des données d'une manière non-orienté objet. Accesseurs n'encapsulent pas efficacement quoi que ce soit en ce qu'il ya peu de différence pratique entre les deux morceaux de code suivant:

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

Et ceci:

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

Les deux extraits de code exposent l'idée qu'une personne est étroitement couplé aux cheveux. Ce couplage étroit se révèle alors dans toute la base de code, ce qui dans le logiciel fragile. Autrement dit, il devient difficile de changer la façon dont les cheveux d'une personne est stockée.

Au lieu de cela:

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

Cela fait suite à la prémisse de « dire, ne demandez pas. » En d'autres termes, les objets doivent être instruits (par d'autres objets) pour effectuer une tâche spécifique. Tel est le point entier de la programmation orientée objet. Et très peu de gens semblent le faire.

La différence entre les deux scénarios est le suivant:

  • Dans la première situation, Bob avait aucun contrôle sur quelle couleur ses cheveux deviendrait. Idéal pour un coiffeur avec un penchant pour les rousses, pas si grand pour Bob qui méprise cette couleur.
  • Dans la deuxième situation, Bob a un contrôle complet sur la couleur ses cheveux deviendra parce qu'aucun autre objet dans le système peut changer cette couleur sans la permission de Bob.

Une autre façon d'éviter ce problème est de renvoyer une copie de la couleur des cheveux de Bob (comme une nouvelle instance), qui n'est plus couplé à Bob. Je trouve que pour être une solution inélégante, car cela signifie qu'il ya un comportement autre désirs de classe, en utilisant les cheveux d'une personne, qui ne sont plus associés à la personne elle-même. Cela réduit la capacité de réutiliser le code, ce qui conduit à un code dupliquée.

Hiding Types de données

En Java, qui ne peut pas avoir deux signatures de méthode qui ne diffèrent que par le type de retour, il ne cache pas vraiment le type de données sous-jacentes utilisées par l'objet. Vous rarement, sinon jamais, voir ce qui suit:

public class Person {
  private long hColour = 1024;

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

En règle générale, les variables individuelles ont leur type de données exposé in extenso par l'utilisation de l'accesseur correspondant, et nécessite refactoring pour le changer:

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

Bien qu'il offre un niveau d'abstraction, il est un voile mince qui ne fait rien pour le couplage lâche.

Tell, Do not Ask

Pour plus d'informations sur cette approche, lisez Tell, Do not Ask .

Exemple de fichier

Considérez le code suivant, légèrement modifié de la réponse de 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 );
   }
}

La méthode getType() dans ce cas est redondant et inévitablement (en pratique) conduisent à un code dupliquée, comme:

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

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

Questions:

  • Type de données. L'attribut type ne peut pas changer facilement d'une chaîne à un entier (ou une autre classe).
  • Protocole Implicite. Il est temps de faire abstraction du type spécifique de la (PNG, JPEG, TIFF, EPS) au général (IMAGE, DOCUMENT, SPREADSHEET).
  • Introduit Bugs. La modification du protocole implicite ne génère pas une erreur de compilation, ce qui peut conduire à des bogues.

Contournez le problème tout à fait en empêchant les autres classes de demander pour les données:

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

Cela implique de changer la méthode accesseur 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() );
   }
}

Aucun autre code dans le système cassera lorsque la classe File transitions du protocole implicite de types spécifiques (par exemple, JPEG) à des types génériques (par exemple, l'image). Tout le code dans le système doit utiliser la méthode isValidType, qui ne donne pas le type de l'objet appelant, mais dit laclasse File pour valider un type.

L'idée est que si vos classes client appel à des fonctions get / set, vous pouvez changer ce qu'ils font plus tard et les appelants sont isolés. Si vous avez une variable publique, et je y accéder directement, il n'y a aucun moyen pour vous d'ajouter un comportement plus tard lors de l'accès ou d'un ensemble.

Même dans votre exemple simple, vous pouvez prendre plus profiter.

Au lieu d'utiliser:

milesPerGallon = miles / gallons;

dans calculateMileage ()

Vous pouvez changer de mettre à jour milesPerGallon quand ils ont été appelés setMiles () et setGallons (). Ensuite, retirez setMilesPerGallon () pour indiquer qu'il est une propriété en lecture seule.

Le point est qu'une classe ne doit pas permettre un accès direct à ses champs, parce que c'est spécifique à la mise en œuvre. Vous voudrez peut-être changer la classe plus tard afin d'utiliser un autre stockage de données, mais garder la classe même pour ses « utilisateurs », ou vous pouvez créer une interface qui ne peut pas inclure des champs non plus.

Jetez un oeil à la Wikipedia article sur le sujet.

Ils fournissent une interface publique pour votre classe, et une certaine mesure d'encapsulation. Considérez comment vous accéder à des données publiques accesseurs.

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

Maintenant, si vous décidez que vous voulez ajouter une validation à votre classe, vous devez changer votre code partout que les champs étaient directement accessibles. Si vous utilisez juste accesseurs dès le départ ( que si elles sont nécessaires ) vous pouvez éviter cet effort, et seulement changer votre code dans un seul endroit.

En utilisant des accesseurs vous donne la possibilité de changer la mise en œuvre ultérieure. Vous ne pourriez pas penser que vous avez besoin, mais parfois vous faites. Par exemple, vous pouvez utiliser le modèle proxy à la charge paresseuse un objet qui est coûteux à utiliser:

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

Si cela arrive souvent à jouer est quand vous avez des objets mis en correspondance avec les bases de données à travers un ORM. Vous chargez seulement les choses dont vous avez besoin, puis revenir à la base de données pour charger le reste si / quand il est effectivement utilisé.

En général setters et getters étaient une mauvaise bidouille par les premiers constructeurs de GUI (Borland) pour contourner le fait que toutes les variables doivent être privées (vraiment, cela est absolument nécessaire)

Certains les appellent une abstraction, mais ils ne sont pas. Un setter / getter boilerplate est pas mieux qu'un membre du public. Ils permettent un accès complet à la variable parfois la classe ne peut pas contrôler et restreindre encore les changements dans la classe (si votre variable est un entier, il faut encore changer tout ce qui appelle le setter et getter de changer la variable à une chaîne )

accesseurs et de mutateurs ENCOURAGER accéder aux données d'une classe à l'extérieur de la classe. Tout code qui accède à un membre d'une classe doit exister probablement dans cette classe (comme vos états de conception) et ne devrait donc pas besoin setters ou accesseurs. Ils doivent être inutiles.

forçant également un Setter dans toutes vos classes est horrible, cela signifie que vos classes ne peuvent simplement pas être immuable alors que vous devriez vraiment avoir une très bonne raison de faire une classe mutable.

Cela dit, ils sont un peu utiles pour les problèmes transversaux tels que les moteurs de Persistance et les constructeurs de l'interface graphique où ils peuvent obtenir et définir les valeurs et la classe peuvent suivre ce qui a été obtenu ou modifié et modifier ou valider.

Un meilleur modèle pour les systèmes qui ont besoin de la transversale d'accès variable serait d'accéder à la variable directement par la réflexion, mais d'appeler un poseur ou d'un apporteur le cas échéant -. Le poseur et getter privé si possible

Cela permettrait au code non-OO TRANSERSALES pour fonctionner correctement, permettrait à votre classe de modifier des ensembles et obtient quand il a besoin et d'en permettre getters (qui sont parfois très utile) si nécessaire.

Le point de méthodes accesseurs ie. accesseurs est encapsuler AKA cacher l'information. Il est l'un des principes de base de la programmation orientée objet.

méthodes accesseurs

Informations cacher / encapsulation

La réponse en un mot est interfaces .

Interfaces permettent méthodes, pas les champs, de sorte que la convention établie est d'avoir des méthodes GetX et setX à cet effet.

(et interfaces est la façon de découpler la fonctionnalité de mise en œuvre en Java)

Votre exemple est extrême à l'absurde. Oui, tous ces accesseurs gonflent le code et ajouter aucune valeur dans ce cas. Mais l'idée sous-jacente d'encapsulation est conçu pour les grands systèmes composés de plusieurs composants qui interagissent, et non pas pour les petits, des programmes autonomes.

Caractéristiques des utilisations utiles, sensibles des accesseurs:

  • Une classe qui est utilisée par de nombreuses autres classes (cacher les détails de mise en œuvre rend plus facile pour les clients)
  • accesseurs uniquement pour les champs pour lesquels ils sont en fait nécessaires - aussi peu que possible, la plupart des champs devraient être privés et utilisés que dans leur classe
  • Très peu setters en général: les champs mutables rendent beaucoup plus difficile de garder une trace de l'état du programme que les champs en lecture seule
  • accesseurs qui en fait faire quelque chose en plus d'accéder à un Fied, par exemple setters qui jettent des exceptions pour les valeurs non valides ou mettre à jour un horodatage « Dernière modification », ou un getter qui calcule une valeur à la volée plutôt que de compter sur un champ sous-jacent

Avance rapide de quelques mois. Peut-être que votre professeur vous demande de mettre en œuvre une version à distance de la classe Milage. Peut-être en tant que service Web, peut-être autre chose.

Sans getter / setters, vous auriez à changer tous les codes partout où acccesses une Milage, avec les getter / setters vous à peu près (dans un monde parfait atleast) ont juste besoin de changer la création d'un type de milage.

accesseurs et de mutateurs vous permettent de créer des raccourcis utiles pour l'accès et des données au sein d'un muter objet. En général, cela peut être considéré comme une alternative à avoir deux fonctions avec un objet qui sont utilisés pour obtenir et définir une valeur, comme suit:

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

L'avantage évident du code JavaScript de cette manière est que vous pouvez utiliser des valeurs obscures que vous ne voulez pas l'utilisateur d'accéder directement. Un résultat final à la recherche quelque chose comme ce qui suit (en utilisant une fermeture pour stocker la valeur d'une nouvelle construction terrain):

function Field(val){
    var value = val;

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

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

Ajout Setter et méthodes Getter Pour l'état du bean géré accessible, vous devez ajouter des méthodes setter et getter pour cet état. La méthode createSalutation appelle la méthode bean'sgreet et la méthode getSalutation récupère le résultat. Une fois que les méthodes setter et getter ont été ajoutés, la fève est terminée. Le code final ressemble à ceci: salutations de paquets;

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

Encapsulation et réutilisation capacité de code est la beauté de la programmation orientée objet. Si nous avons affaire à des données sensibles dans notre code, puis nous déclarons comme champs de données privées à dire que nous encapsulons nos données afin que personne ne puisse y accéder directly.Now celui qui veut accéder à ces champs de données doivent utiliser setters et getters -à-dire un mécanisme de contrôle d'accès pour traiter les champs de données sensibles. L'exemple suivant peut aider à comprendre l'avantage et l'importance du compositeur et getters.

  • J'ai mis en place une classe dans laquelle je me sers de jours variable.
  • Dans ma classe, personne ne peut définir la valeur des jours plus de 365.
  • Quelqu'un veut hériter de ma classe. (Code réutilisabilité).
  • Maintenant, quand il entre dans la valeur des jours plus de 365, alors toutes les fonctionnalités de ma classe échouera.
  • D'où i aurait dû déclarer les jours variables comme champ de données privées.
  • Maintenant, si je l'avais déclaré champ de données de jours comme privé, alors personne ne pouvait définir la valeur des jours plus de 365 comme je l'aurais mis en place un setter fonctions avec limitations mentionnées au sujet de l'entrée.
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top