Classes desburocratizadas: Fim dos getters e setters pobres

Para expor o estado interno dos objetos ao mundo exterior, algumas linguagens se valem de um conceito chamado propriedade. Vou começar falando das linguagens que não tem propriedades como o Java. Estas linguagens consideram que as variáveis e constantes tem propósito apenas interno, apesar de poderem ser declaradas como públicas. Caso caiamos na tentação de usá-las como membros públicos para simplificar e desburocratizar a programação, compramos uma dor-de-cabeça futura.

O problema nesta abordagem é que quebrarmos o encapsulamento, expondo o interior do objeto como ele realmente é. Assim, se a representação interna mudar, obrigatoriamente, os clientes externos vão ser afetados. Isso é o indesejado alto acomplamento que propaga as alterações e amarra os objetos, reduzindo o reuso. Também temos o problema de não podermos controlar a atribuição, fazendo validações. Assim, podemos ter aluno.nota = 11 ! Outra possibilidade é engessar a classe. A própria Sun cometeu uma gafe ao criar a classe java.awt.Rectangle com campos públicos. Daí, depois não pôde evitar que façamos um rect.height = -100, que é bastante esquisito ! O retângulo é tão baixo que o topo está abaixo da base ?!?! E se ela os transformasse em campos privados quebrando a compatibilidade ? Já pensou ter que pedir para os usuários não fazerem upgrade de jvm porque senão o programa não vai mais rodar ? Por causa disso, espalhamos uma porção de getters e setters pelo código da classe por pura burocracia. O Java herdou essa filosofia do C++.

O conceito de propriedades não é nada novo, nem revolucionário. O velho Delphi já tinha propriedades. Para não mexer com a poeira do fundo do baú, vamos deixar o Delphi de lado e ver como o Scala expõe o estado interno sem burocracia e com escalabilidade: “Tornar o simples fácil e o difícil possível”.

Suponha o mesmo caso da classe Rectangle. Vejamos como ficaria a classe com a suposta quebra de encapsulamento:


class Rectangle {
   var x, y, width, height: Int
}

val rect = new Rectangle
rect.height = -100

Então, percebido o erro depois de esta classe estar sendo usada em vários programas, precisamos corrigi-lo. Felizmente, Scala tem um mecanismo sofisticado para permitirmos interferir na leitura e gravação da variável, sem alterar o uso externo da mesma. Vejamos como ficaria:


Class Rectangle {
    var x = 0
    var y = 0
    private var pwidth = 0
    private var pheight = 0

    def width_=(arg: Int) {
      if (arg < 0) throw new IllegalStateException("width não pode ser menor que zero" )
      pwidth = arg
    }
    def width = pwidth

    def height_=(arg: Int) {
      if (arg < 0) throw new IllegalStateException("height não pode ser menor que zero" )
      pheight = arg
    }
    def height = pheight
  }

val rect = new Rectangle
rect.height = -100  // Causa uma IllegalArgumentException

Assim, ganhamos o controle quando precisarmos, garantindo a manutenção da interface externa da classe para os clientes dela. É admissível que x e y possam receber valores negativos por estarem à esquerda do eixo y ou abaixo do eixo x. Assim, não precisamos da burocracia e poluição de getters e setters pobres desnecessários mas requeridos nas linguagens que não tem propriedades.

Bonus track

Por baixo dos panos, no bytecode, na verdade todos os campos ficam privados e têm os métodos de leitura e escrita.Vejam como ficou a classe Rectangle “descompilada” (sem implementação) para Java:


public class scalaintegration.Rectangle
   extends java.lang.Object implements scala.ScalaObject{
 private int pheight;
 private int pwidth;
 private int y;
 private int x;
 public scalaintegration.Rectangle();
 public int height();
 public void height_$eq(int);
 public int width();
 public void width_$eq(int);
 private void pheight_$eq(int);
 private int pheight();
 private void pwidth_$eq(int);
 private int pwidth();
 public void y_$eq(int);
 public int y();
 public void x_$eq(int);
 public int x();
 public int $tag();
 }

Alexei Barbosa de Aguiar

Deixe um comentário