Não use Getters e Setters

Você está ferindo o encapsulamento

John Gomes
4 min readOct 8, 2021

Utilizei nos exemplos a linguagem de programação Java. Porém os conceitos abordados nesse texto, podem ser aplicados à qualquer linguagem Orientada à Objetos.

Pra quem trabalha com linguagens orientadas a objetos, esse título pode parecer estranho a princípio, mas vou explicar o problema desses dois métodos tão utilizados no dia-a-dia do desenvolvimento de software.

Primeiro precisamos lembrar o que nos motiva a criá-los:

O Encapsulamento

Logo nas primeiras aulas de Orientação a Objetos, você aprende que encapsular os atributos da sua classe é uma boa prática porque isso impede que agentes externos tenham acesso direto aos atributos que não os pertencem, podendo manipulá-los de forma indevida. Então, logo te ensinam a colocar o modificador de acesso private nos atributos e pronto, estão protegidos!

public class Conta {
private Usuario usuario;
private Tipo tipo;
private Double saldo;
}

Mas chega a hora que você precisa dessa classe e vai precisar preencher seus atributos. Porém, como fazer isso se eles são privados ? Nesse momento entra em ação o método Set.

Você cria um método que recebe um valor e o repassa ao atributo privado.

public class Conta {
private Usuario usuario;
private Tipo tipo;
private Double saldo;

public void setTipo(Tipo tipo) {
this.tipo = tipo;
}

public void setSaldo(Double saldo) {
this.saldo = saldo;
}
}

Esse método, do jeito que está escrito, acabou de quebrar o seu encapsulamento. Se o objetivo com o encapsulamento era não permitir com que uma outra classe consiga manipular os valores dos atributos da classe Conta, acabamos de falhar:

public class ContaService {

public void deposita(Double valor, Conta conta) {
conta.setSaldo(valor);
}

public void estorna(Double valor, Conta conta) {
Double valorAtual= account.getSaldo();
conta.setSaldo(valorAtual + valor);
}
}

Repare que temos dois métodos que manipulam o saldo da conta. Um “seta” o saldo do jeito que recebeu e o outro soma o valor recebido ao valor que já havia anteriormente. E agora, qual a regra correta ?

Se é um projeto onde você trabalha sozinho, você será capaz de responder essa pergunta facilmente. Mas se você trabalha em equipe, é muito fácil cada um implementar de maneiras diferentes, gerando inconsistência.

Então como resolver?

O melhor seria a classe Conta ter em vez de um método Set, um método Add por exemplo, que receba um valor e faça as validações e conversões necessárias, afinal é um atributo da classe Conta, nada mais natural que ela conheça as regras e validações dos seus próprios atributos.

public class Conta {
private Usuario usuario;
private Tipo tipo;
private Double saldo;

public void addSaldo(Double valor) {
if(this.Tipo == Tipo.PREMIUM){
this.saldo = this.saldo + valor;
} else {
this.saldo = valor;
}
}
}

Mas você pode estar se perguntando; Se eu fizesse essa mesma lógica no método Set, não daria no mesmo ? Do ponto de vista de lógica de programação, sim. Mas do ponto de vista semântico, está errado.

Se traduzirmos para o português a palavra Set, significa Definir, e não é o que queremos fazer aqui.

Não queremos definir um novo valor para o atributo Saldo. O que queremos é, passar um valor e deixar que a classe Conta decida o que fazer (somar, substituir, aplicar desconto etc…).

Parece um detalhe bobo, afinal, é apenas um nome. Mas um programador ao invocar o método Set, espera que esse método faça o que o nome diz: Que ele Defina.

Fazer com que o desenvolvedor, toda vez que precise invocar um método, tenha que abrir a classe onde este se encontra, para ler e entender o que ele faz, para assim saber o que esperar dele é um dos grandes erros apontados logo no segundo capitulo, denominado “Nomes Significativos” do classíco Clean Code de Robert C. Martin.

“O nome de uma variável, função ou classe deve responder a todas as grandes questões. Ele deve lhe dizer porque existe, o que faz e como é usado. Se um nome requer um comentário, então ele não revela seu propósito.”

Clean Code — Robert C. Martin

E o Get ?

Vamos lembrar que em linguagens como o Java trabalhamos com referências de objetos, então quando você faz um método Get, você não está apenas devolvendo um valor e sim a referência do objeto. Desse modo quem recebe essa referência pode manipular esse valor como bem entender.

public class ContaService {

public void estorno(Double valor, Conta conta) {
Usuario usuario = conta.getUsuario();
Documento documento = usuario.getDocument();

//Nesse ponto o número do documento pode ser alterado por referência
documento.setNumeber(123123);
}
}

Isso quer dizer que a menos que você deixe os atributos da sua classe Conta imutáveis e tenha a certeza que todas as classes declaradas como membros também tenham seus atributos imutáveis, não adianta nada você não ter métodos Setters e criar métodos Getters, o problema continua.

Para evitar isso, podemos utilizar de técnicas, como as Cópias Defensivas. Básicamente, consiste em, ao em vez de devolver a instância do objeto, criar cópias do objeto original:

public class Conta {
private Usuario usuario;
private Tipo tipo;
private Double saldo;

public Usuario getUsarioCopy() {
return new Usuario(this.user);
}
}

Conclusão

Sabemos que nem sempre é possível evitar o uso de métodos desse tipo, porque muitos frameworks para evitar o uso excessivos de reflection nos obrigam a criar esse métodos. Mas em outras classes como BO's, DTO's, VO's etc.. É possível, e aconselho, que se evite fazer esse tipo de método sempre que possível.

Referências no texto

Código Limpo — Robert C. Martin (2008) ; Capitulo 2: Nomes Significativos

--

--

John Gomes

Programador, pai da Luiza, músico fracassado, amante de tecnologia. Quando comecei a trabalhar com isso Adobe Flash era o máximo!