首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

用 JavaServer Faces 2 实现可扩展 UI (2)

用 JavaServer Faces 2 实现可扩展 UI (2)

技巧 1:遵守 DRY 原则在我作为软件开发人员从事的第一项工作中,我的任务是为基于 UNIX® 的计算机辅助设计和计算机辅助制造(CAD/CAM)系统实现一个 GUI。
最初,一切进行顺利,但是一段时间后,我的代码开始问题不断。待到代码发布的时候,系统已经相当脆弱,我甚至都害怕修复 bug,而这次的代码发布自然也伴随着一连串的 bug 报告。
如果我在这个项目中遵循了 DRY 原则 — 不重复自己(Don't Repeat Yourself),我本可以让自己不至于这么悲惨。DRY 原则最初由 Dave Thomas 和 Andy Huntprinciple 提出(参见 ),它要求:
每条知识都必须在系统内具有一个单一、清晰和权威的表示。
我的 CAD/CAM 应用程序并不符合 DRY 原则 — 它具有太多关注点之间的交叉 — 因此在一个地方所做的更改常常会在其他地方引起意想不到的更改。
JSF 1 在几个方面违背了 DRY 原则,比如,它强迫您提供托管 beans 的两种表示 — 一个使用 XML,一个使用 Java 代码。对多重表示的需求让创建和更改托管 bean 更加困难。正如我在本系列  中介绍的,JSF 2 让您能够使用注释取代 XML 来配置托管 bean,这样一来,托管 bean 就具有了一个单一、权威的表示。
除托管 beans 之外,就连一些看似有益的实践 — 比如在所有视图中包括相同的样式表 — 也违背了 DRY 原则,并会导致混乱。比如,如果要更改样式表的名字,就必须更改多个视图。如果可能,最好是封装此样式表包含。
DRY 原则同样适用于代码设计。如果多个方法均包含遍历树的代码,一种好的做法是(比如在一个子类中)封装遍历树的算法。
在实现 UI 时,因大多数更改均在开发过程中发生,所以遵守 DRY 原则尤其重要。
JSF 2 模板JSF 2 在很多方面都支持 DRY 原则,其中之一就是通过模板。模板能够封装在应用程序视图中十分常见的功能,因此该功能只需被指定一次。在 JSF 2 应用程序中,一个模板可供多个组装(compositions)用于创建视图。
我在  中所介绍的 places 应用程序具有三个视图,如图 1 所示:
图 1. places 应用程序的视图:Login、source viewer 和 places与很多 Web 应用程序一样,这个 places 应用程序包含多个具有相同布局的视图。JSF 模板功能让您能够在一个模板内封装该布局 — 及其他共享工件,比如 JavaScript 和 Cascading Style Sheets(CSS)。清单 1 是  中所示的这三个视图的模板:
清单 1. places 模板:/templates/masterLayout.xhtml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
   xmlns:ui="http://java.sun.com/jsf/facelets">

  <h:head>
    <title>
      <ui:insert name="windowTitle">
        #{msgs.placesWindowTitle}
      </ui:insert>
    </title>
  </h:head>
   
  <h:body>  
    <hutputScript library="javascript" name="util.js" target="head"/>      
    <hutputStylesheet library="css" name="styles.css" target="body"/>      
     
    <div class="pageHeading">
      <ui:insert name="heading">
        #{msgs.placesHeading}
      </ui:insert>     
    </div>
      
    <div class="menuAndContent">
      <div class="menuLeft">
        <ui:insert name="menuLeft"/>
      </div>   
         
      <div class="content" style="display: #{places.showContent}">
        <ui:insert name="content"/>
      </div>
         
      <div class="menuRight">
        <ui:insert name="menuRight">
          <ui:include src="/sections/shared/sourceViewer.xhtml"/>
        </ui:insert>
      </div>
    </div>  
  </h:body>
</html>




中的模板为此应用程序的所有视图提供了如下的基础设施:
  • HTML <head>、<body> 和 <title>
  • 一个默认标题(可由使用此模板的那些组装覆盖)
  • 一个 CSS 样式表
  • 某些实用 JavaScript
  • 一个布局,格式为 <div>,以及对应的 CSS 类
  • 头的默认内容(可被覆盖)
  • 右菜单的默认内容(可被覆盖)
正如  所示,模板通过 <ui:insert> 标记将内容插入到布局中。
如为 <ui:insert> 标记指定了主体,正如我在  中为窗口标题、头和右菜单所做的,JSF 会将此标记的主体作为默认内容。借助 <ui:define> 标记,使用此模板的那些封装可以定义内容或者覆盖默认内容,如清单 2 所示,它给出了 login 视图的标记:
清单 2. login 视图
1
2
3
4
5
6
7
8
9
10
11
12
13
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
   xmlns:ui="http://java.sun.com/jsf/facelets"
   template="/templates/masterLayout.xhtml">
   
  <ui:define name="menuLeft">
    <ui:include src="/sections/login/menuLeft.xhtml"/>
  </ui:define>

  <ui:define name="content">
    <ui:include src="/sections/login/content.xhtml"/>           
  </ui:define>
      
</ui:composition>




这个 login 视图为窗口的标题、头和右菜单使用了模板的默认内容。它只定义了特定于此 login 视图的功能:内容部分和左菜单。
通过为窗口标题、头或右菜单提供 <ui:define> 标记,我也可以覆盖此模板的默认内容。比如,清单 3 显示了这个 source-viewer 视图( 中间的图片):
清单 3. source-viewer 视图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
   xmlns:ui="http://java.sun.com/jsf/facelets"
   template="/templates/masterLayout.xhtml">

  <ui:define name="content">
    <ui:include src="/sections/showSource/content.xhtml"/>
  </ui:define>

  <ui:define name="menuLeft">
    <ui:include src="/sections/showSource/menuLeft.xhtml"/>      
  </ui:define>
      
  <ui:define name="menuRight">
    <ui:include src="/sections/showSource/menuRight.xhtml"/>      
  </ui:define>

</ui:composition>




source-viewer 视图定义了内容部分以及右菜单的内容。它还覆盖了由  中的模板定义的针对左菜单的默认内容。
清单 4 显示了 places 视图( 底部的图片):
清单 4. places 视图
1
2
3
4
5
6
7
8
9
10
11
12
13
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
   xmlns:ui="http://java.sun.com/jsf/facelets"
   template="/templates/masterLayout.xhtml">

   <ui:define name="menuLeft">
    <ui:include src="/sections/places/menuLeft.xhtml"/>
   </ui:define>

  <ui:define name="content">
    <ui:include src="/sections/places/content.xhtml"/>
  </ui:define>

</ui:composition>




JSF 2 模板功能模板功能背后的概念十分简单。定义一个模板来封装在多个视图中常见的功能。每个视图由一个组装和一个模板组成。
当 JSF 创建视图时,它加载组装的模板,然后将由组装所定义的内容插入模板。

请注意清单 、和  之间的相似性。所有这三个视图均指定模板并定义内容。另外,也请注意创建新视图十分容易,因为大多数视图的基础设施都封装在模板及所包含的文件内。
使用 JSF 模板功能的另一个有趣之处是类似清单 、 和  中的这些视图并不会随时间有太多变化,所以大部分视图代码基本不需要维护。
与使用模板的视图类似,模板本身也更改甚少。由于大量常见功能都封装在几乎不用维护的代码中,这样一来,您就可以将精力集中于视图的实际内容 — 比如,login 页面的左菜单应该有些什么内容。专心于视图的实际内容就是下一个技巧的主旨所在。
返回列表