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
