Scala: os cuidados com encapsulamento

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?

5 Comentários

  1. Michael Nascimento Santos 26/07/2011 at 10:47 #

    E nunca use Double para este caso de uso, e sim BigDecimal!

  2. Ronualdo 26/07/2011 at 13:54 #

    Na verdade, tanto eu acho que seria interessante a linguagem fornecer uma opção só de getter para var, como eu me surpreendi quando descobri que a linguagem não fornecia essa opção.

    Minha pergunta é se existe algum motivo para não fornecer essa opção.

    Alguem pode fazer essa sugestão ao Odersky

  3. Ronualdo 26/07/2011 at 14:07 #

    Alguem pode fazer essa sugestão ao Odersky?

  4. Rodrigo Araujo TI 26/07/2011 at 14:33 #

    Sim, seria interessante ter um modificador que tornasse apenas o getter publico. Isto existia no finado JavaFX Script na forma de um modificador de acesso “public-read”. JavaFX Script tinha recursos interessantes como este que poderiam ser adotados por linguagens como Scala. Imagino que seria interessante existir modificadores de acesso get e set e conjuntamente haver uma forma de definir métodos que sobrescrevessem quando necessário estes getters e setters implícitos.

  5. Ronualdo 27/07/2011 at 09:47 #

    Se houvesse uma forma de sobreescrever os getters e setters implícitos, eu já ficaria extremamente satisfeito.

Deixe uma resposta