Um dos grandes conhecedores da linguagem Java chamado Joshua Block já dizia há bastante tempo que o fator mais importante que distingue um módulo bem projetado de um mal projetado é o grau com que o módulo oculta de outros módulos os seus dados internos e outros detalhes da implementação.

Dessa forma, podemos concluir que uma das características essenciais de projetar um certo módulo, é definirmos a sua capacidade de ocultar todos os seus detalhes de implementação. Isso é importante, pois daria aos módulos uma comunicação somente através da sua API sem que um módulo não precise conhecer o funcionamento interno dos outros módulos.

No restante do artigo será visto como podemos criar um bom projeto definindo algumas regras para os campos nas classes públicas e privadas.

Acessores e Modificadores para Classes Públicas

Quando temos uma classe pública com seus métodos sendo diretamente acessados, dizemos que ela não oferece os benefícios do encapsulamento. O Encapsulamento nos oferece a ideia de tornar o software mais flexível, fácil de modificar e de criar novas implementações. O Encapsulamento oferece um controle de acesso aos atributos e métodos de uma classe. É uma forma eficiente de proteger os dados manipulados dentro da classe, além de determinar onde esta classe poderá ser manipulada.

O exemplo abaixo demonstra uma classe mal projetada:

Listagem 1: Classe mal projetada expondo diretamente as instâncias.


public class Ponto {
	public double x;
	public double y;
}
 

Da forma como está mostrado acima nós não podemos alterar a representação sem alterar a API. Não há uma forma de tomar medidas quando o campo é acessado. Por exemplo, se a partir de agora surgir uma nova regra que um determinado campo deve ser somado sempre 5 ao seu valor, isto poderia ser manipulado por algum acessador externo e perderíamos esse controle.

Porém, existe uma regra bastante clara sobre essas classes públicas, devemos sempre fornecer campos privados métodos Acessores (getters) públicos, e caso a classe seja mutável devemos também fornecer os métodos modificadores (setters). Abaixo temos a nova representação da classe acima:

Listagem 2: Classe encapsulada com métodos Acessores e modificadores.


public class Ponto {
	private double x;
	private double y;

	public Ponto(double x, double y) {
		this.x = x;
		this.y = y;
	}

	public double getX() { return x; }

	public double getY() { return y; }

	public void setX(double x) { this.x = x; }

	public void setY(double y) { this.y = y; }

}
 

Portanto, se uma classe puder ser acessada de fora do seu pacote sempre devemos fornecer métodos Acessores para preservarmos a flexibilidade de alterar a representação interna da classe. Se uma classe pública expor os campos de dados, como demonstrado na listagem um, provavelmente nunca conseguiremos alterar a sua representação, visto que o código cliente já deve ter sido amplamente distribuído.

Muitas vezes os programadores reclamam que é um pouco chato criar os acessores e modificadores para cada um dos campos. No entanto, as IDEs mais utilizadas já nos oferecem essas facilidades, tornando automática a sua geração. Para ilustrar isso vamos considerar o Eclipse. Após definir os campos da nossa classe e marcarmos eles como privados, como demonstra a imagem abaixo, vamos agora gerar os setters (modificadores) e getters (Acessores) da nossa classe.

Classe criada na IDE Eclipse

Figura 1: Classe criada na IDE Eclipse.

Após criar uma classe simples com dois campos, devemos gerar os setterns e getters, para isso clique com o botão direito do mouse dentro da classe que foi criada e seleciona a opção “Source” e por fim “Generate Getters and Setters...” conforme ilustra a imagem abaixo:

Selecionando a geração automática dos setters e getters no Java

Figura 2: Selecionando a geração automática dos setters e getters no Java.

Na próxima tela aparecerá na janela “Generate Getters and Setters” cada um dos campos da nossa classe criada. Ainda podemos expandir os campos e aparecerá um checkbox para selecionar se desejamos apenas criar setter ou getters. Selecione o checkbox principal para que ele já selecione automaticamente os outros checkbox e clique em OK. A figura abaixo ilustra o processo:

Gerando os getters e setters para os campos da classe.

Figura 3: Gerando os getters e setters para os campos da classe.

Agora podemos notar que o Eclipse gerou getters e setters para cada um dos campos da nossa classe facilitando o nosso trabalho e evitando erros que procedimentos manuais oferecem. A listagem abaixo mostra como ficou a nossa classe:

Listagem 3: Classe com setters e getters gerados automaticamente pelo eclipse.


public class Ponto {

	private double x;
	private double y;
	
	public double getX() {
		return x;
	}
	public void setX(double x) {
		this.x = x;
	}
	public double getY() {
		return y;
	}
	public void setY(double y) {
		this.y = y;
	}
	
}

Na janela “Generate Getters and Setters” podemos notar que também há outras opções como “Insertion point:” na qual podemos definir onde os getters e setters criados serão colocados. Por exemplo, podemos definir para eles serem colocados logo abaixo de algum campo ou método que já exista na classe. Também temps a opção “Sort by” que define se desejamos criar primeiro os setter e depois os getters ou primeiro os getters e posteriormente os setter. Tudo isso fica a critério do desenvolvedor. Outra opção é o “Acess modifier” que podemos escolher se ele será public, protected, default ou private e ainda podemos definir se ele será final ou synchronized. No nosso caso eles serão públicos e não serão nem final e nem synchronized.

Por fim, a última opção “Generate method comments” gera os getters e setters com alguns comentários padrões. Desenvolvedores que gostam de documentar seus códigos ou acharam essa necessidades dos getters e setters que estão prestes a gerar podem explorar essa opção. Se gerássemos com essa opção sinalizada teríamos a listagem abaixo:

Listagem 4: Setters e Getters gerados com a opção de comentários selecionada.


public class Ponto {

	private double x;
	private double y;
	/**
	 * @return the x
	 */
	public double getX() {
		return x;
	}
	/**
	 * @param x the x to set
	 */
	public void setX(double x) {
		this.x = x;
	}
	/**
	 * @return the y
	 */
	public double getY() {
		return y;
	}
	/**
	 * @param y the y to set
	 */
	public void setY(double y) {
		this.y = y;
	}
	
}

Outra facilidade que o Eclipse nos oferece é a possibilidade de criarmos Acessores e modificadores para um campo apenas, que por exemplo, foi adicionado posteriormente na nossa classe. Por exemplo, imaginando que temos a classe acima e agora definimos um novo campo chamado “descrição” que deveria ser incluído uma simples descrição pelo usuário. Após adicionar o campo na tela apenas selecionamos este campo e fazemos o mesmo procedimento clicando com o botão direito do mouse em cima do campo selecionado e selecionando a opção “Source” e “Generate Getters and Setters”. A figura abaixo demonstra o processo:

Criando Acessores e modificadores para um campo exclusivo.

Figura 4: Criando Acessores e modificadores para um campo exclusivo.

Na janela “Generate Getters and Setters” podemos notar que o campo já vem previamente marcado para gerar os seus modificadores e Acessores. Basta dar um OK e ele será gerado. Apenas uma observação interessante é que agora poderíamos usar a opção “Insertion point” deixando ele gerar os getters e setters do novo campo abaixo do último método gerado. Por fim, teríamos o código abaixo criado:

Listagem 5: Getters e setters criados para um campo exclusivo.


public class Ponto {

	private double x;
	private double y;
	private String descricao;
	
	public double getX() {
		return x;
	}
	public void setX(double x) {
		this.x = x;
	}
	public double getY() {
		return y;
	}
	public void setY(double y) {
		this.y = y;
	}
	public String getDescricao() {
		return descricao;
	}
	public void setDescricao(String descricao) {
		this.descricao = descricao;
	}

	
}

Note que se a opção “Insertion point” não for usada os métodos criados poderão ficar mal posicionados, dando um aspecto um pouco sujo na classe.

Acessores e modificadores para Classes Privadas de Pacote

Se tivermos uma classe privada de pacote (default, sem modificador de acesso) ou então uma classe aninhada privada, não há problemas em expormos os seus campos de dados. Essa é uma forma que gera menos confusão visual do que a abordagem de métodos Acessores, tanto na definição da classe quanto no código clientes que a usa. Diferentemente do caso anterior, agora o cliente está vinculado à representação interna da classe, no entanto, como o cliente está vinculado ao pacote em que a classe está contida isto não é um problema, diferente de antes que tínhamos diversos clientes distribuídos em variados lugares acessando os campos da classe. Assim, se uma alteração na representação for necessária, é possível faze-la sem afetar nenhum código fora do pacote. Se a classe for aninhada privada, o escopo de uma alteração é ainda mais restrita, visto que, apenas envolve a classe delimitadora.

Várias bibliotecas da classe Java violam a advertência de que classes públicas não deveriam expor seus campos. Uma dela é a classe Dimension do pacote java.awt. Esse erro de projeto custou problemas de desempenho que persiste até hoje. Portanto, devemos ter muito cuidado na hora de projetar nossas classes e métodos sempre seguindo as dicas já discutidas.

Conclusão

Neste artigo estudamos que as classes públicas nunca devem expor campos mutáveis. Apenas de ser menos perigoso expor campos diretamente quando eles são imutáveis, a regra padrão deve ser seguida. No entanto, quando temos classes privadas de pacote ou classes aninhadas privadas, às vezes é desejável expor os seus campos, sejam mutáveis ou imutáveis.

Bibliografia