jump to navigation

Demonstrando o uso de encapsulamento ao facilitar o uso do componente rich:tree do Richfaces 02/08/2009

Posted by fredericobenevides in JSF, Seam.
7 comments

Quando começamos a programar em java, muitas pessoas ensinam que para criar uma classe encapsulada é apenas colocar os seus atributos como private e criar getters/setters para cada um deles. Geralmente, essas pessoas no início não aprendem que encapsulamento é aquele objeto que provê operações públicas ocultando suas implementações internas, que não precisam ser conhecidas por quem usa. Um exemplo real disso, é uma pessoa utilizar um carro conhecendo apenas as suas “operações” externas (volante, acelerador, marcha, etc…) sem ter necessidade de conhecer o seu funcionamento interno.

Então, eu resolvi criar um post pra demonstrar o uso de encapsulamento ao criar uma classe que facilitará na criação de árvores.

Como criar uma árvore usando RichFaces

Para quem não conhece o funcionamento do componente rich:tree, darei breves explicações de como utilizá-lo. Essas implementações foram feitas usando o framework Seam.

Os códigos abaixo terão sempre o mesmo resultado na geração da seguinte árvore:
rich_tree

Para criar uma árvore, utilizamos o TreeNodeImpl. Cada nó da árvore devemos setar a descrição utilizando o método setData do TreeNodeImpl. É simples, porém ao adicionar cada filho de um nó, devemos preocupar com o seu identificador.

public TreeNode getArvore() {

	/* Raiz */
	TreeNode rootNode = new TreeNodeImpl();

	/* adicionando o pai1 na raiz */
	TreeNodeImpl pai1 = new TreeNodeImpl();
	pai1.setData("Pai_1");
	rootNode.addChild(0, pai1);

	/* adicionando filho1 ao pai1 */
	TreeNodeImpl filhoPai1 = new TreeNodeImpl();
	filhoPai1.setData("Filho_1");
	pai1.addChild(0, filhoPai1);

	/* adicionando filho2 ao pai1 */
	filhoPai1 = new TreeNodeImpl();
	filhoPai1.setData("Filho_2");
	pai1.addChild(1, filhoPai1);

	/* adicionando o pai2 na raiz */
	TreeNodeImpl pai2 = new TreeNodeImpl();
	pai2.setData("Pai_2");
	rootNode.addChild(1, pai2);

	/* adicionando filho1 ao pai2 */
	TreeNodeImpl filhoPai2 = new TreeNodeImpl();
	filhoPai2.setData("Filho_1");
	pai2.addChild(0, filhoPai2);

	/* adicionando filho2 ao pai2 */
	filhoPai2 = new TreeNodeImpl();
	filhoPai2.setData("Filho_2");
	pai2.addChild(1, filhoPai2);

	/* adicionando filho3 ao pai2 */
	filhoPai2 = new TreeNodeImpl();
	filhoPai2.setData("Filho_3");
	pai2.addChild(2, filhoPai2);

	return rootNode;
}

A página contendo o componente rich:tree que chama a árvore.

<rich:tree style="width:300px" value="#{tree.arvore}" var="item">
 <h:outputText value="#{item}"/>
</rich:tree>

Agora que já temos um conhecimento de como trabalhar com o componente, podemos ver que é até simples o uso, apesar de termos um pequeno trabalho ao adicionar cada filho. Logo, resolvi criar uma classe para auxiliar na construção do objeto que representa a árvore, e também para que o desenvolvedor não perdesse tempo em estudar como implementar essa construção. Afinal, quanto menor o trabalho do desenvolvedor em se preocupar com cada detalhe, mais ele agiliza o desenvolvimento.

A seguir a nova implementação usando a classe Tree para auxiliar na criação da árvore.

public class Tree<T> {

	private boolean root;
	private String description;
	private T object;

	private TreeNode<Tree<T>> treeNode;
	private List<Tree<T>> childs = new ArrayList<Tree<T>>();

	public Tree() {
		treeNode = new TreeNodeImpl<Tree<T>>();
		root = true;
	}

	public Tree(String description, T object) {
		treeNode = new TreeNodeImpl<Tree<T>>();
		this.object = object;
		this.description = description;
		treeNode.setData(this);
	}

	public void addChild(Tree<T> tree) {
		childs.add(tree);
	}

	public TreeNode<Tree<T>> toTreeNode() {
		int i = 0;
		for (Tree<T> tree : childs) {
			TreeNodeImpl<Tree<T>> nodeChild = (TreeNodeImpl<Tree<T>>) tree.toTreeNode();
			treeNode.addChild(i, nodeChild);
			i++;
		}
		return treeNode;
	}

	public boolean isRoot() {
		return root;
	}

	public T getObject() {
		return object;
	}

	public String getDescription() {
		return description;
	}

	@Override
	public String toString() {
		return description;
	}
}

public TreeNode<Tree<String>> getArvore() {

	/* criando a raiz */
	Tree<String> parentRoot = new Tree<String>();

	/* adicionando o pai1 na raiz */
	Tree<String> pai1 = new Tree<String>("Pai_1", null);
	parentRoot.addChild(pai1);

	/* adicionando filho1 ao pai1 */
	Tree<String> filhoPai1 = new Tree<String>("Filho_1", null);
	pai1.addChild(filhoPai1);

	/* adicionando filho2 ao pai1 */
	filhoPai1 = new Tree<String>("Filho_2", null);
	pai1.addChild(filhoPai1);

	/* adicionando o pai2 na raiz */
	Tree<String> pai2 = new Tree<String>("Pai_2", null);
	parentRoot.addChild(pai2);

	/* adicionando filho1 ao pai1 */
	Tree<String> filhoPai2 = new Tree<String>("Filho_1", null);
	pai2.addChild(filhoPai2);

	/* adicionando filho2 ao pai1 */
	filhoPai2 = new Tree<String>("Filho_2", null);
	pai2.addChild(filhoPai2);

	/* adicionando filho3 ao pai1 */
	filhoPai2 = new Tree<String>("Filho_3", null);
	pai2.addChild(filhoPai2);

	return parentRoot.toTreeNode();
}

Como podemos ver, o código ficou mais simples e mais limpo para a criação da árvore. Para adicionar cada filho não houve necessidade de preocupar com o identificar numérico. A codificaçao ficou mais simples, porém produtivo.

Os getters foram criado apenas onde houve necessidade. Caso também fosse permitido o acesso ao treeNode, quebraria o encapsulamento, pois o desenvolvedor poderia modificar seus dados, sendo que apenas a classe Tree é o responsável para modificar.

Por fim, a classe está bem encapsulada, já que o desenvolvedor não tem necessidade de conhecer o funcionamento interno, ele tem apenas que conhecer as suas operações públicas.