Scala: os cuidados com encapsulamento

Scala: os cuidados com encapsulamento
gas
gas

Compartilhe

Um dos pontos difíceis de lidar em qualquer sistema está ligado com quebra de encapsulamento. Em Java, uma vez que o padrão de uma variável membro permite que ela seja acessada por fora do objeto, protegemos os dados através do modificador private e um getter:

 public class Conta { private List<Movimentacao> movimentacoes = new ArrayList<Movimentacao>(); private double saldo;

public double getSaldo() { return saldo; }

public List<Movimentacao> getMovimentacoes() { return movimentacoes; } } 

Enquanto a criação de getters é um padrão amplamente adotado, muitas vezes ainda deixamos vazar os objetos mutáveis, como no caso da lista acima. Para não vazar referências a tais objetos, quebrando o encapsulamento e possívelmente alguma funcionalidade interna, retornamos uma cópia segura da mesma:

 public List<Movimentacao> getMovimentacoes() { // protegendo a lista interna para não quebrar o encapsulamento return Collections.unmodifiableList(movimentacoes); } 

http://www.flickr.com/photos/fxtreme/235948470/ A criação de getters e setters deve ser feita com cuidado: uma vez que a implementação é exposta através de um getter e setter, a remoção dele, para finalmente encapsulá-lo, implica na mudança de todas as chamadas a ele. Em Ruby, o mesmo cuidado pode ser tomado:

 class Conta def movimentacoes @movimentacoes.dup end end 

Em Scala, existem diversas variações para declaração de variáveis membro. A padrão que ainda nos ajuda a criar good citizens, é a definição de uma variável privada, final:

 class Conta(saldo:Double) 

A desvantagem é que as vezes é interessante colocar um getter para expor o conteúdo referenciado. No caso de uma conta bancária, é interessante expor o saldo da mesmo, mas não uma lista de movimentações sem protegê-la.

Para isso, podemos definir uma val (equivalente a variáveis final em Java), uma vez que o papel do getter é gerado automaticamente pelo compilador (nomes de variáveis e métodos estão no mesmo contexto, portanto não requerem recompilação quando um substitui o outro):

 class Conta(val saldo:Double) 

A desvantagem pode estar em desejar alterar o saldo da conta e não poder. Nesse caso definiríamos o membro como var:

 class Conta(var saldo:Double) 

A desvantagem nessa abordagem é a criação do getter e do setter de exposição: ao definir uma variável como "private" mas permitir o acesso a mesma, o encapsulamento é novamente quebrado. Portanto, para membros cuja referência pode ser alterada (vars), é interessante gerar o "getter" mas não expor o setter para não quebrar o encapsulamento, como em:

 class Conta(private var \_saldo:Double) { def saldo = \_saldo } 

Mas nesse caso, o código deixa de ser curto como nas abordagens onde o encapsulamento é mais facilmente quebrado. O mesmo é válido para muitas linguagens: é mais fácil quebrar o encapsulamento dos objetos do que executar a troca de mensagens para efetuar uma transferência:

 class Conta(private var \_saldo:Double) { def saldo = \_saldo def debita(valor:Double) { movimentacoes.add(new Movimentacao(-valor)); \_saldo -= valor } } 

O encapsulamento é a base de orientação a objetos. Nos momentos em que é adequado trabalhar de forma OO, é ele que permite a troca de mensagens entre objetos prevenindo o usuário de depender de como o tipo funciona internamente. Das maneiras demonstradas aqui, escolha a maneira de encapsular suficientemente adequada para faciltiar a manutenção de suas classes a longo prazo (para evitar quebras inesperadas).

Como pergunta, será que seria interessante a linguagem fornecer uma opcao só de getter para var?

Veja outros artigos sobre Inovação & Gestão