Pergunta

Esta pergunta já tem uma resposta aqui:

Por favor, perdoe o comprimento, mas aqui estão dois programas, exatamente o mesmo, mas um com e um sem setters, getters e construtores.

Eu já tomei uma aula básica de C ++ antes e não me lembro de nada disso, e no momento não estou vendo o ponto deles, se alguém pudesse explicá -los nos termos de Lamen, eu apreciaria muito isso .. . No momento, eles parecem nada mais do que desperdícios de espaço para fazer meu código parecer mais longo, mas o professor diz que é importante (e até agora é isso).

Desde já, obrigado! E agora aqui está o código: milhas.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);
    }
}

MileeGest.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 agora para aquele sem setters e 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);
    }
}

TestmileeAgetest.java:

package gasMileage;

public class Testmileagetest 
{

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

}

Obrigado novamente!

Foi útil?

Solução

O ponto de getters e setters, independentemente da linguagem, é ocultar a variável subjacente. Isso permite que você adicione a lógica de verificação ao tentar definir um valor - por exemplo, se você tivesse um campo para uma data de nascimento, poderá permitir que a definição desse campo até algum tempo no passado. Isso não pode ser aplicado se o campo for acessível ao público e modificável - você precisa dos getters e setters.

Mesmo se você ainda não precisar de verificação, poderá precisar no futuro. Escrever os getters e setters agora significa que a interface é mantida consistente; portanto, o código existente não será interrompido quando você o alterar.

Outras dicas

As outras respostas geralmente dão uma boa idéia de alguns motivos para usar getters e setters, mas quero dar um exemplo um pouco completo de por que eles são úteis.

Vamos tomar, por exemplo, um arquivo (ignorando a existência de um File classe em java). este File A classe tem um campo para armazenar o tipo de arquivo (.pdf, .exe, .txt, etc) ... ignoraremos todo o resto.

Inicialmente você decide armazená -lo como um String Sem getters e setters:

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

Aqui estão alguns problemas em não usar getters e setters.

Sem controle sobre como o campo está definido:

Qualquer cliente da sua classe pode fazer o que quiser com ela:

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

Você decide mais tarde que provavelmente não quer que eles façam isso ... mas como eles têm acesso direto ao campo da sua classe, não tem como impedi -lo.

Incapacidade de alterar facilmente a representação interna:

Mais tarde ainda, você decide que deseja armazenar o tipo de arquivo como uma instância de uma interface chamada FileType, permitindo que você associe algum comportamento a diferentes tipos de arquivos. No entanto, muitos clientes da sua classe já estão recuperando e definindo tipos de arquivos como Strings. Então, você teria um problema lá ... você quebraria muito código (mesmo código em outros projetos que você não pode consertar, se for uma biblioteca) se você acabou de mudar o campo de um String para um FileType.

Como getters e setters resolvem isso

Agora imagine que você fez o campo de tipo private e criado

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

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

Controle sobre a definição da propriedade:

Agora, quando você deseja implementar um requisito de que apenas certas seqüências de caracteres sejam tipos de arquivos válidos e evitem outras seqüências, você pode apenas escrever:

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
}

Capacidade de alterar facilmente a representação interna:

Alterando a String A representação do tipo é relativamente fácil. Imagine que você tem um enum ValidFileType que implementa FileType e contém os tipos válidos de arquivos.

Você pode alterar facilmente a representação interna do tipo de arquivo na classe como esta:

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

Desde que os clientes da classe estão ligando getType() e setType() De qualquer forma, nada muda da perspectiva deles. Somente os internos da classe mudaram, não a interface que outras classes estão usando.

Encapsulation

Accessor methods ("setters and getters") attempt to hide the details about how the data in an object is stored. In practice, they are a glorified means to store and retrieve data in a non-object-oriented fashion. Accessors do not effectively encapsulate anything in that there is little practical difference between the following two pieces of code:

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

And this:

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

Both code snippets expose the idea that a Person is tightly coupled to Hair. This tight coupling then reveals itself throughout the code base, resulting in brittle software. That is, it becomes difficult to change how a Person's hair is stored.

Instead:

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

This follows the premise of "tell, don't ask." In other words, objects should be instructed (by other objects) to perform a specific task. This is the whole point of object-oriented programming. And very few people seem to get it.

The difference between the two scenarios is this:

  • In the first situation, Bob had no control over what colour his hair would become. Great for a hair stylist with a penchant for redheads, not so great for Bob who despises that colour.
  • In the second situation, Bob has complete control over what colour his hair will become because no other object in the system is allowed to change that colour without Bob's permission.

Another way to avoid this problem is to return a copy of Bob's hair colour (as a new instance), which is no longer coupled to Bob. I find that to be an inelegant solution because it means there is behaviour that another class desires, using a Person's hair, that is no longer associated with the Person itself. That reduces the ability to reuse code, which leads to duplicated code.

Hiding Data Types

In Java, which cannot have two method signatures that differ only by return type, it really does not hide the underlying data type used by the object. You will seldom, if ever, see the following:

public class Person {
  private long hColour = 1024;

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

Typically, the individual variables have their data type exposed verbatim by use of the corresponding accessor, and requires refactoring to change it:

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

While it provides a level of abstraction, it is a thin veil that does nothing for loose coupling.

Tell, Don't Ask

For more information on this approach, read Tell, Don't Ask.

File Example

Consider the following code, slightly modified from ColinD's answer:

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

The method getType() in this instance is redundant and will inevitably (in practice) lead to duplicated code such as:

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

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

Issues:

  • Data Type. The type attribute cannot readily change from a String to an integer (or another class).
  • Implied Protocol. It is time consuming to abstract the type from the specific (PNG, JPEG, TIFF, EPS) to the general (IMAGE, DOCUMENT, SPREADSHEET).
  • Introduces Bugs. Changing the implied protocol will not generate a compiler error, which can lead to bugs.

Avoid the problem altogether by preventing other classes from asking for data:

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

This implies changing the get accessor method to 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() );
   }
}

No other code in the system will break when the File class transitions the implied protocol from specific types (e.g., JPEG) to generic types (e.g., IMAGE). All the code in the system must use the isValidType method, which does not give the type to the calling object, but tells the File class to validate a type.

The idea is that if your client classes call get/set functions, you can change what they do later and the callers are insulated. If you have a public variable, and I access it directly, there is no way for you to add behavior later when it is accessed or set.

Even in your simple example, you could take more advantage of it.

Instead of using:

milesPerGallon = miles / gallons;

in calculateMileage()

You could change setMiles() and setGallons() to update milesPerGallon when they were called. Then, remove setMilesPerGallon() to indicate that it's a read-only property.

The point is that a class should not allow direct access to its fields, because this is implementation-specific. You may want to change the class later in order to use another data storage, but keep the class the same for its "users", or you may want to create an interface which cannot include fields either.

Have a look at the Wikipedia article on the subject.

They provide a public interface for your class, and some measure of encapsulation. Consider how you would access public data without getters and setters.

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

Now if you decide you want to add some validation to your class, you have to change your code everywhere that the fields were directly accessed. If you just use getters and setters from the start (only where they are needed) you can avoid that effort, and only change your code in one place.

Using getters and setters gives you the flexibility to change the implementation later. You might not think you need that, but sometimes you do. For example, you might want to use the Proxy pattern to lazy load an object that is expensive to use:

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

Where this often comes to play is when you have objects mapped to databases through an ORM. You only load the stuff you need, then go back to the database to load the rest if/when it is actually used.

In general setters and getters were a bad hack by the early GUI builders (borland) to get around the fact that all variables should be private (Really, this is absolutely necessary)

Some people call them an abstraction, but they are not. A boilerplate setter/getter is no better than a public member. They still allow full access to the variable at times the class can't control and still restrict in-class changes (if your variable is an int, you still have to change everything that calls the setter and getter to change the variable to a string)

Getters and Setters encourage accessing the data of a class from outside the class. Any code that accesses a member of a class should probably exist within that class (as your design states) and therefore shouldn't need setters or getters. They SHOULD be unnecessary.

Also forcing a Setter into all your classes is horrid, it means that your classes simply can't be immutable whereas you should actually have a Really Good Reason to make a class mutable.

That said, they are kind of useful for cross-cutting concerns like persistance engines and GUI builders where they can get and set values and the class can monitor what was got or changed and modify or validate it.

A better pattern for those systems that need the cross-cutting variable access would be to access the variable directly through reflection BUT to call a setter or getter if one exists--making the setter and getter private if possible.

This would allow the non-OO cross-cutting code to work correctly, would allow your class to modify sets and gets when it needs to and allow getters (which are sometimes really useful) where necessary.

The point of accessor methods ie. getters and setters is to provide encapsulation AKA information hiding. It's one of the basic principles of object oriented programming.

Accessor methods

Information hiding/encapsulation

The answer in one word is interfaces.

Interfaces allow for methods, not fields, so the established convention is to have getX and setX methods for this purpose.

(And interfaces is the way to decouple functionality from implementation in Java)

Your example is extreme to the point of absurdity. Yes, all those getters and setters bloat the code and add no value in that case. But the underlying idea of encapsulation is meant for larger systems composed of many interacting components, not for small, self-contained programs.

Characteristics of useful, sensible uses of getters and setters:

  • A class that is used by many other classes (hiding implementation details makes it easier for the clients)
  • Getters and setters only for fields for which they're actually needed - as few as possible, most fields should be private and used only within their class
  • Very few setters in general: mutable fields make it much harder to keep track of the program's state than read-only fields
  • Getters and setters that actually do something besides accessing a fied, e.g. setters that throw exceptions for invalid values or update a "last modified" timestamp, or a getter that computes a value on the fly rather than relying on an underlying field

Fast forward a few months. Maybe your teacher asks you to implement a remote version of the Milage class. Maybe as a web service, maybe something else.

Without the getter/setters, you'd have to change every code everywhere that acccesses a Milage, with the getter/setters you pretty much(in a perfect world atleast) just have to change the creation of a Milage type.

Getters and Setters allow you to build useful shortcuts for accessing and mutating data within an object. Generally, this can be seen as an alternative to having two functions with an object that are used to get and set a value, like so:

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

The obvious advantage to writing JavaScript in this manner is that you can use it obscure values that you don't want the user to directly access. A final result looking something like the following (using a closure to store the value of a newly constructed Field):

function Field(val){
    var value = val;

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

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

Adding Setter and Getter Methods To make the state of the managed bean accessible, you need to add setter and getter methods for that state. The createSalutation method calls the bean’sgreet method, and the getSalutation method retrieves the result. Once the setter and getter methods have been added, the bean is complete. The final code looks like this: package greetings;

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 and code reuse-ability is the beauty of object oriented programming. If We are dealing with some sensitive data in our code then we declare it as private data fields i.e. we encapsulate our data so that no one can access it directly.Now any one who wants to access those data fields must make use of setters and getters i.e. a controlled access mechanism to deal with sensitive data fields. Following example can be helpful in understanding the advantage and importance of setter and getters.

  • I have implemented a class in which i am making use of days variable.
  • In my class no one can set the value of days more than 365.
  • Some one wants to inherit from my class.(code re-usability).
  • Now when he enters the value of days more than 365, then all functionality of my class will fail.
  • Hence i should have declared the days variable as private data field.
  • Now if i had declared days data field as private then no one could set the value of days more then 365 as i would have implemented a setter functions with mentioned limitations about input.
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top