Os métodos de uma classe devem chamar seus próprios getters e setters?

53

Onde trabalho, vejo muitas classes que fazem coisas assim:

public class ClassThatCallsItsOwnGettersAndSetters {

    private String field;

    public String getField() {
        return field;
    }

    public void setField(String field) {
        this.field = field;
    }

    public void methodWithLogic() {
        setField("value");
        //do stuff
        String localField = getField();
        //do stuff with "localField"
    }
}

Se eu escrevesse isso do zero, teria escrito o methodWithLogic()seguinte:

public class ClassThatUsesItsOwnFields {

    private String field;

    public String getField() {
        return field;
    }

    public void setField(String field) {
        this.field = field;
    }

    public void methodWithLogic() {
        field = "value";
        //do stuff            
        //do stuff with "field"
    }
}

Eu sinto que quando a classe chama seus próprios getters e setters, torna o código mais difícil de ler. Para mim, quase implica que uma lógica complexa está acontecendo nessa chamada de método, embora, no nosso caso, quase nunca ocorra. Quando estou depurando algum código desconhecido, quem pode dizer que o bug não é um efeito colateral nesse método? Em outras palavras, isso me faz fazer muitas viagens paralelas na jornada de compreensão do código.

Existem benefícios para o primeiro método? O primeiro método é realmente melhor?

Daniel Kaplan
fonte
Quando estou depurando algum código desconhecido, quem pode dizer que o bug não é um efeito colateral nesse método? Sua unidade testa. :)
Joshua Taylor
11
O uso de getters / setters no seu código interno permite criar um ponto de interrupção no seu código se você estiver passando por um depurador. Ele também permite que você verifique se algo está errado com a definição de configuração / obtenção de um valor, você sabe que é por causa desse método e não por causa de algum acesso inválido. No geral, ele fornece mais código de consistência.
precisa saber é o seguinte

Respostas:

46

Não vou dizer o que é melhor ou o pior, porque isso depende em parte da sua situação (na minha opinião). Mas considere que seus getters e setters podem alterar a implementação posteriormente, e ignorá-los ignoraria essa lógica.

Por exemplo, o que acontece se você adicionar um sinalizador "sujo" a alguns campos de incubação posteriormente? Ao chamar seus setters em seu código interno, você definirá o sinalizador sujo sem alterar nenhum outro código. Em muitas situações, isso seria uma coisa boa.

Matt S
fonte
Mas e quando você está inicializando os dados no back-end e não deseja que isso seja interpretado como sujo. Se você ligou setField()desde o início, acabou de introduzir um bug.
Daniel Kaplan
6
Certo, é por isso que digo que depende em parte da situação. Talvez sua inicialização de dados deva ignorar os configuradores, mas o outro código lógico não. Escolha regras apropriadas e cumpra-as.
Matt S
3
@ DanielKaplan: o ponto é que chamar getters e setters é, na maioria dos casos, a coisa certa a se fazer, e somente em situações específicas não. No entanto, no código do mundo real, quando você altera uma implementação de getter / setter existente posteriormente para introduzir alguns efeitos colaterais intencionais, provavelmente terá que verificar todas as chamadas para o getter ou setter dentro da classe e também todo acesso direto ao campo. É por isso que você deve tentar manter as aulas o menor possível.
Doc Brown
33

Ligar para o setter diretamente não é, por si só, um problema. A pergunta realmente deveria ser: Por que nosso código tem setters em todos os lugares?

Classes mutáveis ​​são um jogo perigoso, particularmente onde a própria classe gerencia seu próprio estado. Considere quantos desses levantadores realmente precisam existir. Quantos poderiam ser definidos no construtor e depois serem encapsulados inteiramente pela própria classe?

Se o campo deve ser configurável externamente, pergunte a si mesmo se o método com lógica deve estar lá. Sua própria classe é realmente apenas uma classe de dados / estado? Esta classe tem mais de uma responsabilidade?

Não me interpretem mal, haverá casos em que isso está bem. Não estou dizendo que você deva eliminar completamente os setters em classes com lógica. Mas estas devem ser a exceção, não a regra. E, nesses poucos casos, deve ser óbvio qual acessar diretamente.

pdr
fonte
11
Eu concordo totalmente / pratico essa linha de pensamento. Eu também acho que você levanta um ponto que é de fato mais importante do que minha pergunta. Dito isto, não acho que responda diretamente à minha pergunta original. A menos que você esteja dizendo, "no grande esquema das coisas, sua pergunta não importa". O que também pode ser verdade :) #
454 Daniel Kaplan
@ DanielKaplan: Estou dizendo exatamente isso. :) Fiquei dividida entre responder e comentar com este, mas realmente não acho que exista uma resposta correta que não faça a pergunta maior.
quer
9

Sim, os métodos da sua classe devem chamar os getters e setters. O ponto principal de escrever getters e setters é a prova do futuro. Você pode transformar todas as propriedades em um campo e expor diretamente os dados aos usuários da classe. A razão pela qual você cria os getters e setters não é necessariamente porque existe uma lógica complexa agora, mas para que a interface não seja quebrada no futuro, se você precisar adicioná-la.

Michael
fonte
11
Não vejo a relação entre sua primeira frase e o resto do seu parágrafo. A primeira frase diz que uma classe deve chamar seus próprios getters e setters. O restante do parágrafo explica por que o código do cliente (ou seja, código usando a classe) deve usar os getters e setters em vez de acessar os campos diretamente. Mas não vejo como é por isso que a classe não deve acessar diretamente seus próprios campos.
Daniel Kaplan
11
@DanielKaplan Meu argumento é que as razões são a mesma. Que, se você adicionar a lógica do setter posteriormente, ela afeta o código interno tanto (potencialmente) quanto o externo.
Michael Michael
2
@ Michael: Muitas vezes, pode haver boas razões para uma classe não usar seus próprios getters / setters. Um cenário comum é o local em que existem muitos setters do formulário. someField=newValue; refresh(); Se o método permitir que vários campos sejam definidos, chamar setters para gravar esses campos causaria operações redundantes de "atualização". Escrever todos os campos e depois chamar refresh()uma vez pode render uma operação mais eficiente e de aparência mais suave.
Supercat
6

Para responder suas perguntas em uma palavra, sim.

Ter uma classe chamando seus próprios getters e setters adiciona extensibilidade e fornece uma base melhor para o código futuro.

Digamos que você tenha algo parecido com isto:

public class Vehicle
{
    private int year;
    private String make;

    public Vehicle(int year, String make)
    {
        setYear(year);
        setMake(make);
    }

    public void setYear(int year)
    {
        this.year = year;
    }

    public void setMake(String make)
    {
        this.make = make;
    }
}

Chamar os setters por ano e make atualmente pode não adicionar nenhuma funcionalidade, mas e se você quiser adicionar algo como validação de entrada aos setters?

public class Vehicle
{
    private int year;
    private String make;

    public Vehicle(int year, String make)
    {
        setYear(year);
        setMake(make);
    }

    public void setYear(int year)
    {
        if(year > 0)
        {
            this.year = year;
        }
        else
        {
            System.out.println(year + " is not a valid year!");
        }
    }

    public void setMake(String make)
    {
        this.make = make;
    }
}
Zach Latta
fonte
5

Uma coisa que não foi mencionada é que getter e setters (como todos os métodos) são virtuais em Java. Isso adiciona outro recurso para sempre usá-los em seu código. Outro usuário pode estender sua classe, substituindo seus getters e setters. Sua classe base usaria os dados da subclasse em vez de seus próprios. Em um idioma em que você marca explicitamente funções virtuais, isso é muito mais útil, pois você pode prever e declarar com quais funções isso pode ser feito. Em Java, isso é algo que você sempre deve estar ciente. Se o comportamento for desejado, usá-los em seu código é uma coisa boa, caso contrário, não muito.

Jonathan Henson
fonte
3

Se você estiver tentando realizar algo que é fornecido pela interface pública, use os getters / setters.

No entanto, como proprietário / desenvolvedor da classe, você tem permissão para acessar as seções particulares do seu código (de dentro da classe, é claro), mas também é responsável por atenuar os perigos.

Portanto, talvez você tenha um getter que itera sobre alguma lista interna e deseja obter o valor atual sem incrementar o iterador. Nesse caso, use a variável privada.

public class MyClass
{
    private int i;
    private List<string> list;
    public string getNextString()
    {
        i++;
        return list[i];
    }

    private void getString()
    {
        // Do not increment
        string currentString = list[i];

        // Increment
        string nextString = getNextString();
    }
}
ConditionRacer
fonte
Você pode dar o raciocínio para isso?
Daniel Kaplan
1

Sim. Os getters e setters representam o estado, então vire essa questão - você deseja acompanhar várias maneiras de alterar o estado de um objeto da mesma maneira, a menos que precise?

Quanto menos coisas você precisar acompanhar melhor - é por isso que objetos imutáveis ​​são mais fáceis de lidar.

Considere, não há nada que o impeça de ter um campo público e um getter / setter público - mas o que você ganha?

Por ocasião, pode ser desejável ou mesmo necessário acessar diretamente o campo por um ou mais motivos; você não deve se esquivar disso, se acontecer. Mas você realmente deve fazê-lo apenas se houver um benefício definido.

jmoreno
fonte
O ponto da minha pergunta é que estou apenas perguntando sobre o acesso direto dos campos na mesma classe. Eu já entendo o propósito de getters e setters para o código do cliente.
Daniel Kaplan
@ DanielKaplan: Eu entendo isso, e o que estou dizendo é que, tanto quanto possível, você deve tratá-los da mesma forma.
jmoreno
@DanielKaplan: Você poderia ter um levantador privado e um público, que têm exatamente o mesmo resultado (todos os efeitos colaterais são iguais) pelo menos para o momento, mas o que isso lhe traria além do potencial de divergir as coisas? A diferença entre este cenário e o que você descreve é que você não pode evitar ser capaz de acessar o exterior estado dos getters / setters, mas você pode evitar realmente fazê-lo. Não complique seu código, a menos que você precise.
jmoreno
1

Ambos os métodos têm seus casos de uso. Como o setter público mantém o valor do campo (e / ou valores vinculados) consistente, você deve usar o setter quando a lógica do método não interferir nessa lógica de consistência. Se você acabou de definir sua "propriedade", use setter. Por outro lado, há situações em que você precisa de acesso direto a alguns campos, por exemplo, operações em massa com setter pesado ou operações em que o conceito do setter é muito simples.

É sua responsabilidade manter as coisas consistentes. O setter faz isso por definição, mas não pode cobrir casos complexos.

Artur
fonte
1

Eu diria que não ..

Se seus getters / setters apenas obtêm e configuram (sem inicialização lenta, sem verificações, etc), basta usar suas variáveis. Se, no futuro, você alterar seus getters / setters, poderá muito bem mudar seu methodWithLogic()(porque a classe está mudando) e poderá chamar um getter / setter em vez de uma atribuição direta. Você pode documentar esta chamada para o getter / setter (pois será estranho chamá-lo quando o restante do código da sua classe estiver usando diretamente a variável).

A JVM incluirá chamadas frequentes para getters / setters. Portanto, nunca é um problema de desempenho. O ganho do uso de variáveis ​​é legibilidade e IMHO, eu iria para ele.

Espero que isto ajude..

Pravin Sonawane
fonte