Não use Getters e Setters
Você está ferindo o encapsulamento
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