Existe uma lei que rege todos os lugares do mundo, em todos os planos e dimensões. Esta lei é a Lei de Murphy, dizendo que você pode morrer e virar um superpolicial robô chamado Robocop. Brincadeiras a parte, a Lei de Murphy diz basicamente que se algo pode dar errado, isto irá dar errado, ou seja, você não deve deixar nenhuma fresta onde o erro pode ocorrer. O seu criador, Edward A. Murphy, um engenheiro aeroespacial, dizia que nunca devemos deixar uma brecha no projeto, pois será neste ponto onde ocorrerá a falha, e como muitas outras coisas que vieram do mundo da engenharia para a informática, esta lei também veio para o nosso ambiente.
Muitos dos erros que ocorrem em sistemas orientados a objetos são causados por simplesmente a facilidade em inserir parâmetros incorretos ou ter acesso a parâmetros que não deveriam ser expostos de qualquer maneira, sem antes passar por algum tratamento ou algoritmo.
Por exemplo, observe a classe abaixo e tente observar quais erros podem ocorrer:
| |
Como vocês podem ver, essa classe segue alguns princípios básicos de encapsulamento, onde seu atributos são todos privados e o acesso é feito apenas através de métodos. Porém, observando com um pouco de cuidado, podemos notar que não é bem isso que ocorre, observe abaixo um exemplo de utilização da classe acima:
| |
Saida:
Lista de alunos reprovados:
Cascao
Lista de alunos aprovados:
Magali
Monica
Lista de alunos aprovados:
Magali
Monica
Cebolinha
O erro ocorre ao solicitar novamente a lista de alunos aprovados, temos agora um novo aluno, porém que não passou pela avaliação do método adicionaAluno, causando um pequeno transtorno. No contexto desta pequena aplicação já podemos ver que é um erro muito visível, porém pode ser muito pior em uma aplicação distribuída e/ou acessada por diversos clientes ao mesmo tempo, por exemplo, um website.
Apesar de todas as preocupações com a visibilidade dos campos, deixamos passar um meio de acessar diretamente um dos atributos, através de sua referência em memória. O método getAlunosAprovados devolve esta referência quando é invocando, deixando exposto uma parte do nosso objeto. Para tratar este problema, bastaria realizar uma alteração simples, criando uma cópia da lista e devolvendo para o cliente, conforme abaixo:
| |
E a saida agora é:
Cascao
Lista de alunos aprovados:
Magali
Monica
Lista de alunos aprovados:
Magali
Monica
Este exemplo mostra como o encapsulamento vai muito além dos modificadores de acesso. Precisamos claramente, deixar encapsulado também todas as referências aos atributos internos, não adiantando criar métodos que apenas repassem as referências internas dos objetos. Apenas em uma situação realizar esta ação não prejudicará o estado do seu objeto, quando o atributo é final e o objeto é imutável, pois desta forma não há como alterar o objeto referenciado, como no exemplo abaixo:
| |
Da forma como está em cima, o atributo nome poderia até ser publico e mesmo assim não seria possível alterar o seu valor, pois a classe String é imutável e nenhum de seus métodos alterar o seu estado, o que não é o caso, por exemplo, da classe StringBuffer, que faz o contraponto através de seu método append por questões de desempenho.
Como você viu acima, classes imutáveis auxiliam no encapsulamento dos nossos objetos, pois como o estado do objeto não se altera, não existe como inserir informações inconsistentes dentro do objeto, sendo que o máximo que pode ocorrer é a criação de um novo objeto com um estado não coerente. No próximo post vamos falar um pouco mais sobre estes objetos, como construir as suas classes e quais os benefícios.