树形结构在软件系统中是使用频率非常大的一种数据结构,包括一些算法的实现也是基于树形结构来进行的,比如基于二叉树的二分查找法等等。在软件系统中,树形结构更多的体现在树形菜单的构建上。对于树形结构我们都能抽取出一个统一的类结构。比如:
- public class TreeNode{
- private String value;//树节点的值
- private String label;//树节点显示标签
- private boolean leaf;//是否是叶子节点
- private TreeNode parent;//父节点
- private List<TreeNode> childern;//孩子结点集合
- }
通过这个简单的结构我们就能构建一个最基本的树。当然我们使用richfaces,它已经给我们提供了这样的树形结构接口和默认的实现类。我们可以通过使用默认的实现类来构建树,也可以实现它的接口来自定义一个树。
在OECP系统中,无可避免的存在着大量的树形结构,最典型的就是组织结构,这些树形结构充斥在系统的各个角落,作为最常用的,也是非常通用的结构和实现方式,我们需要一个统一的规划和设计来处理,以便减少程序员重复的代码编写,既可以提供未来开发的效率同时也降低了维护的成本。RichFaces提供的<t:tree>已经是一个高度集成的树形结构UI,我们在UI层上的工作除了把类似于组织机构这样常用的组件封装成统一的标签调用之外,我们不需要再做额外的处理。我们需要在数据结构的统一构建上做些文章。在构建之初,我们面临着几个问题。
1、 返回树形结构的数据是不是业务组件的工作,需不需要考虑UI
2、 是一次完成构建树还是动态构建树
针对第一个问题,我认为返回树形结构的数据是不是属于业务范畴很难界定,但是业务组件不应该绑定UI组件,应该是客户端技术无关性的,这就造成业务组件即使封装了树形结构的数据,在UI表现上仍要针对UI组件进行一次树与树的转换,这无疑增加了两端的开发量,造成无谓的消耗。所以,我们不考虑在EJB业务组件端进行树形结构的数据封装,而是直接返回相应的列表。
针对于第二个问题,我们考虑到我们使用技术体系,选择了一次完成构建树。为什么选择这种方案,有什么优缺点呢?影响EJB分布式系统性能的关键是分布式调用带来的网络资源的消耗,要降低这种消耗,就要减少对分布式组件的调用。一次性返回所有的数据就是要减低这种调用EJB的频率。同时一次性构建树,在构建树的工具类中,我们只需要处理这个List就可以了,而无须再次理会调用EJB,适合封装成统一的、适当通用的组件。但是构建过程可能比较麻烦,如果List中包含的实体类的结构不一致,构建的过程更复杂。动态构建树在书写上会比较简单,但是需要EJB组件提供更多的接口,而且由于构建过程中要不断调用EJB,所以很难和EJB调用解耦,无法工具化处理。同时,在OECP项目中,所有的实体类都进行了统一的规划设计,抽象了统一的基础结构和操作,这给我们统一构建树带来了更大的方便。
现在开始介绍基于RichFaces树的封装。
先看看树形结构效果图:
页面代码片段:
- <rich:tree style="width:300px"
- value="#{resourceRegisterAction.resourceTree}" var="item"
- switchType="client" ajaxSubmitSelection="true"
- reRender="info,create,popMsg"
- nodeSelectListener="#{resourceRegisterAction.processSelection}"
- ajaxKeys="#{null}">
- <rich:treeNode>
- <h:outputText value="#{item.name}" />
- </rich:treeNode>
- </rich:tree>
注:#{resourceRegisterAction.resourceTree}要实现Richfaces提供的TreeNode接口的实现ajaxSubmitSelection="true"说明是AJAX的提交方式,reRender="info,create,popMsg"这个很重要,表明该操作返回的数据要渲染的组件,比如回填Form,该处需要Form的ID,这样才能保证数据回填到特定的Form上,否则页面数据不变化。选择树节点需要进行相关的操作,就必须实现nodeSelectListener。
Action构建树代码:
- public TreeNode<FunctionResourceEO> getResourceTree() {
- if (resourceTree == null) {
- try {
- List<FunctionResourceEO> list = ResourceServiceDelegate
- .getInstance().getAllResources();
- FunctionResourceEO func = new FunctionResourceEO();
- func.setName("功能资源");
- resourceTree = new RichTreeConverter<FunctionResourceEO>()
- .list2tree(list, func);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- return resourceTree;
- }
通过ResourceServiceDelegate.getInstance().getAllResources()得到所有的功能资源信息,EJB端只需要将所有的资源列表返回即可,简化了EJB组件数据运算以外的操作。最关键的是new RichTreeConverter<FunctionResourceEO>().list2tree(list, func);通过这个工具类将list转化成RichFaces树的数据结构。该构造过程采用深度优先遍历算法,在效率上还需要改进,希望大家能批评指正。限于篇幅是该工具类的代码和实体抽象类的代码请查看原文:http://www.po-soft.com/blog/yongtree/324.html。
希望该RichFaces自动构建树实现能带给各位朋友以抛砖引玉的作用,有什么问题可以写下您宝贵的留言,大家一起探讨共同进步,谢谢!