Como não aprender Java e Orientação a Objetos: getters e setters

Como não aprender Java e Orientação a Objetos: getters e setters
Paulo Silveira
Paulo Silveira

Compartilhe

Muitas pessoas perguntam: como aprender orientação a objetos?

Há várias maneiras de aprender Programação Orientada a Objetos - POO, creio que não tenha uma melhor, mas existem maneiras de não aprender.

Gerando getters e setters

Uma das práticas mais controversas que aprendemos no início do aprendizado de muitas linguagens orientadas a objeto é a geração indiscriminada de getters e setters. Os exemplos básicos de centenas de tutoriais Java estão recheados com getters e setters da pior espécie: aqueles que não fazem sentido algum. Considere:

class Conta { 
     double limite;
     double saldo; 
} 

Rapidamente os tutoriais explicam o private para o encapsulamento. Mas aí como acessar? Getters e setters nela!

class Conta {
    private double limite; 
    private double saldo; 
    public double getSaldo() { 
         return saldo; 
    } 
    public void setSaldo(double saldo) {
          this.saldo = saldo; 
    } 
    public double getLimite() { 
        return limite; 
    }
    public void setLimite(double limite) { 
        this.limite = limite; 
    } 
} 

Qual é o sentido desse código? Para que esse setSaldo? e esse setLimite? e o getLimite? Você vai usar esses métodos? Nunca crie um getter ou setter sem sentir uma real necessidade por ele.

Isso é uma regra para qualquer método, mas particularmente os getters e setters são campeões: muitos deles nunca serão invocados, e grande parte do restante poderia e deveria ser substituído por métodos de negócios.

Códigos do tipo conta.setSaldo(conta.getSaldo() + 100) se espalharão por todo seu código, o que pode ser muito nocivo: como você vai logar aumentos de saldo, se a forma de alterar saldo de uma conta está espalhada por todo seu sistema?

Tirar dinheiro gera uma situação ainda mais delicada: se precisar verificar se há saldo na conta antes de subtrair um valor do saldo, onde ficará esse if? Espalhado por todo o sistema em diversos pontos? O que acontecerá quando precisar alterar essa regra de negócio?

Segue então a nossa classe Conta reformulada de acordo com essa necessidade:

class Conta {
    private double saldo; 
    private double limite;

    public Conta(double limite) { 
        this.limite = limite; 
    }

    public void deposita (double x) { 
        this.saldo += x; 
    }

    public void saca(double x) { 
        if(this.saldo + this.limite >= x) { 
            this.saldo -= x; 
        } else throw new IllegalArgumentException("estourou limite!"); 
    }

    public double getSaldo() { 
        return this.saldo; 
    } 
} 

E nem estamos falando de test driven development! Testando a classe Conta rapidamente você perceberia que alguns dos getters e setters anteriores não têm uso algum e sentiria falta de alguns métodos mais voltados a lógica de negócio da sua aplicação, como o saca e o deposita.

Esse exemplo é muito trivial, mas você pode encontrar por aí muitas classes que não tem a cara de um java bean que expõem atributos como Connection, Thread, etc, sem necessidade alguma.

Existem sem dúvida práticas piores: utilização de ids para relacionar os objetos, arrays não encapsuladas como estruturas, código fortemente baseado em comparação de Strings hardcoded (que o Guilherme Silveira sarcasticamente batizou de POS, ou programação orientada a strings), milhares de métodos estáticos, entre outros.

Todas essas práticas são comuns quando estamos começando a quebrar o paradigma procedural e confesso já ter sido um grande praticante de algumas. Você só vai utilizar bem o paradigma da orientação a objetos depois de errar muito.

O Phillip Calçado aborda esse tema, chamando essas classes de classes fantoches (puppets). Uma classe fantoche é a que não possui responsabilidade alguma, a não ser carregar um punhado de atributos! Onde está a orientação a objetos? Uma grande quantidade de classes fantoches são geradas quando fazemos Value Objects, entidades do hibernate, entre outros.

Mas o que colocar nas minhas entidades do hibernate/JPA/ORM além de getters e setters?

Antes de tudo, verifique se você realmente precisa desses getters e setters.

Para que um setID na sua chave primária se o seu framework vai utilizar reflection ou manipulação de bytecode para pegar o atributo privado, ou se você pode passá-la pelo construtor? Sobre value objects, você realmente precisa dos seus setters?

Em muitos casos VOs são criados apenas para expor os dados, e não há necessidade alguma para os setters... bastando um bom construtor! Dessa forma evitamos um modelo de domínio anêmico.

No hibernate, e em outros ORMs, costumo colocar alguns métodos de negócio, algo parecido como na classe Conta. Minhas entidades possuem uma certa responsabilidade, em especial as que dizem respeito aos atributos pertencentes a elas.

Para quem ainda está aprendendo, a apostila da caelum de java e orientação a objetos tem algumas dessas discussões e procura ensinar OO comentando sempre dessas más práticas, como por exemplo o uso de herança sem necessidade. Há o artigo de POO da Alura também.

Vale relembrar que o exemplo é apenas didático. Trabalhar com dinheiro exige inúmeros cuidados, como não usar double. O foco desse post é para você parar para pensar antes de criar o getter e o setter. E, claro, pode ser que o seu caso justifique a criação desses métodos.

Vemos bastante esse tópicos nos cursos de Java e de .NET na Alura.

Esse post, criado em 2006 e atualizado em 2017 e depois em 2022, mostra como o design de classes continua seguindo algumas regras de boas práticas importantes. Fundamentos e conceitos são sim essenciais na nossa carreira.

Veja outros artigos sobre Programação