Revisitando a Orientação a Objetos: encapsulamento no Java
4 min4 minutos de leitura
4 min4 minutos de leitura
Autor(a)
maniche
Inscreva-se em nossa Newsletter
Fique por dentro de conteúdos, insights e oportunidades do universo tech. Receba novidades e lançamentos direto no seu e-mail.
Façamos uma aposta. Tenho certeza que você, ao ver a classe abaixo, consegue perceber um problema nela:
classPedido { public String comprador; publicdouble valorTotal; // outros atributos }
Sim. Os atributos estão todos públicos! Isso vai exatamente contra uma das nossas primeiras lições quando aprendemos Java: atributos devem ser privados e precisamos de getters e setters para acessá-los. Vamos então fazer essa mudança no código.
Agora está melhor, certo? Ainda não. Deixamos escapar na verdade o grande princípio que está por trás da ideia de colocar atributos como privados. Do jeito que a classe Pedido está nesse momento, podemos fazer coisas como:
Pedidop=newPedido(); // muda valor do pedido para 200 reais! p.setValorTotal(p.getValorTotal() + 200.0);
Mas aonde está o problema? Imagine outras 10 classes que fazem a mesma coisa: de alguma forma, elas manipulam o valor total do pedido.
Agora imagine que a regra de negócio do pedido mude: todo item comprado ganha desconto de 5% se o valor dele for superior a 1000 reais. Implementar essa mudança não será tarefa fácil. Precisaríamos fazê-la em diferentes classes do sistema.
Quanto tempo demoraremos para mudar o sistema? Não sabemos exatamente aonde devemos fazer as mudanças já que elas estão espalhadas pelo código. Esse, aliás, é um dos grandes problemas de códigos legados: uma simples mudança precisa ser feita em tantas classes e, na prática, sempre esquecemos algum ponto, e nosso sistema frequentemente quebra.
A classe Pedido não foi bem desenhada. Demos acesso direto ao atributo valorTotal, um atributo importante da classe. Veja que o modificador private nesse caso não adiantou de nada, já que demos também um setter para ele. Vamos tentar diminuir o acesso ao atributo, criando métodos mais claros para a operação de depósito:
Mas qual a diferença entre os dois códigos abaixo?
Itemitem=newItem("Super Geladeira", 1500.0);
// antiga if (item.getValor() > 1000) { c1.setValorTotal(c1.getValorTotal() + item.getValor() \* 0.95); } else { c1.setValorTotal(c1.getValorTotal() + item.getValor()); }// nova c1.adiciona(item);
Veja que na primeira linha de código, sabemos exatamente COMO funciona a adição de um novo ítem no pedido: devemos pegar o valor total e somar o valor novo com desconto de 5% se ele for maior que 1000. Já na segunda linha de código, não sabemos como esse processo funciona.
Quando sabemos O QUÊ um método faz (igual ao método adiciona, sabemos que ele adiciona um ítem no pedido, por causa do nome dele), mas não sabemos exatamente como ele faz, dizemos que esse comportamento está encapsulado!
A partir do momento que as outras classes não sabem como a classe principal faz o seu trabalho, significa que as mudanças ocorrerão apenas em um lugar! Afinal, elas estão escondidas (encapsuladas)!
Ou seja, para implementar a regra de negócios nova, bastaria mexermos em um único lugar:
publicvoidadiciona(Item item) { if (item.getValor() > 1000) this.valorTotal += item.getValor(); elsethis.valorTotal += item.getValor() \* 0.95;
// nova regra de negócio aqui }
No fim, a real utilidade do private é esconder acesso de atributos que precisam ser acessados de maneira mais inteligente. Mas veja que de nada adianta colocar todos os atributos como private e criar getters e setters para todos eles. Deixamos o encapsulamento "vazar" do mesmo jeito.
Esconda os atributos, mas pense em comportamentos inteligentes para acessá-los. Uma ótima maneira para saber se o comportamento está encapsulado é olhar para o código que faz uso dele! Se conseguirmos dizer o que o método faz, mas sem dizer como ele faz, então podemos afirmar que o comportamento está encapsulado!