君子博学而日参省乎己 则知明而行无过矣

博客园 首页 新随笔 联系 订阅 管理
摘要
每个web应用开发者一定组织视图组件,例如  页首,正文体和页尾。 有许多技术可以组织这些组件, 但每个都有优缺点。这篇文章包括了7个可用的解决方案并让你了解 Tiles框架和Struts框架的灵活性。从一个简单的说明组织视图组件的例子开始,然后使用 JSP内建的机制,像include, 彻底地解决问题。继续看其他的使用Tiles框架的替换方案,然后学会平衡Tiles框架和Struts框架的协作。 (2,000个字;2002 年1月 4 日)
在Web应用软件开发中,一般用户界面负责站点的外观感觉,它可以让人产生真切的感受。通过界面的展现,来展示应用程序功能和导航。基于Servlet和JSP技术实现用户界面时,当HTML页面被转换为servlets和JSPs时,UI开发人员必须识别哪些是公共的普通HTML页面,哪些是JSP页面组件,如标题、页脚,主体,菜单和搜索等。这篇文章提供了多种有效的解决方案来组织HTML和JSP视图组件。每个方案都使用了特定的标准,像页数、编码重复和布局控制等。
为了探究模板和布局方案,我们使用Tiles 框架.Tiles 框架的视图组件叫做tiles。框架 使用XML配置文件来组织这些tiles.框架不仅能让你再次使用tiles,而且还可以对他们进行设计组织。
为了探寻更强大和灵活的方案,我们研究Tiles框架和Struts 框架之间的协作。Struts是一个使用流行的MVC或者Model 2结构模式的用于开发web 应用软件的开源框架.Struts 有着强大标签库,Tile框架的标签库加入使其更加完善。

评价标准
评价每个方案有如下标准。标准是兼容的。对于特定的情况和特殊的应用,必须经常均衡每个方案中这些因素的优势与缺陷。

页数(Page number)
方案必须尽量使HTML and JSP的页数减到最小。因为页数增加,开发、管理、维护一个应用程序的复杂性将会急剧增加。

代码重复(Code repetiton)
在大多数情况下,重复是不好的。HTML and JSP编码越是重复,开发和维护应用程序就越困难。一个简单变化可能在许多不同页面上导致一连串不可预期结果的改变。具体和实际的获得重新使用的方式应避免编码重复。

布局控制(Layout comtrol)
尽管编码重复是不好的,但是布局逻辑和编码重复的会更糟。在几个JSPs中的扩展视图组件结构的逻辑和行为可以是一个解决灾难的方法。获得模板的设计逻辑的重复使用是一种比仅仅重复使用视图组件更好的形式。因此,你可以通过有效的布局控制达到一个更高的重复使用的水平。

耦合(Coupling)
耦合是个实体间的兼容性的一个度。软件工程师被重复教育要使不相干的类、包等等之间的耦合减到最小。我们可以对视图组件应用相同的原理。尽管从一个使用者角度讲视图组件之间是有区别的,但在JSP执行中,组建可能被杂乱的连接。一种解决方案使必须减少不相干视图组件之间的耦合.

复杂性(Complexity)
复杂性导致了更多的开发和维护费用,也使得更多的复杂方案不合适。当你增加更多的部分时,复杂性同时也在增加,这将使原本看起来简单的和无害的东西很快转变为大混乱。

解决方案
我将使用基本的JSPs例子。通过像header ,footer这样的普通的视图组件来评价一些方案。我将按增加复杂性的顺序给出这些方案,然后相对于评价标准详细的评价每一个。
方案1:基本的JSP
假如a.jsp如下:

<html>
<body>
Header<p>
a‘s body...<p>
Footer<p>
</body>
</html>



假如b.jsp如下: 


<html>
<body>
Header<p>
b‘s body...<p>
Footer<p>
</body>
</html>



    在许多情况,开发者从UI类中直接获取编码,并根据需要直接将他转换成JSP代码。如上所示,每个JSP有重复的HEADER 和FOOTER。方案1是不合需求的,原因是HEADER和 FOOTER视图组件改变,因每页都为所放置的组件负责,所以需要在所有相关页上改变。简单的方案1缺乏远见。有着如此多HTML和JSP 代码重复,我们虽然减少了页的数量,但是需要一个很大的维护费用。就像前面所解释的,不同的视图组件间有着很大的耦合 ,所以这是不合需要的。

方案2:JSP include
假如 a.jsp如下:


<html>
<body>

<%-- include header --%>
<jsp:include page="/header.jsp" />

a‘s body...
<p>

<%-- include footer --%>
<jsp:include page="/footer.jsp" />

</body>
</html>



假如b.jsp如下:


html>
<body>

<%-- include header --%>
<jsp:include page="/header.jsp" />

b‘s body...
<p>

<%-- include footer --%>
<jsp:include page="/footer.jsp" />

</body>
</html>
注意到普通的诸如header和footer这样的视图组件,使用JSP 的include结构使其分开来了。
假如 header.jsp如下:
Header
<p>
假如footer.jsp如下:
Footer 
<p>



    方案2很好的处理了方案1中的一些重要的缺陷。你仅需要改变一次通用的视图组件。因此,这个方案在很大程度上消除了HTML和JSP CODE重复,尤其是提高了应用程序的可维护性。他增加了一些页数,但他彻底的减少了普通视图组件和其他页之间的轻微的耦合.在复杂度上,这个方案可以很简单和容易的使用在现有应用程序中。但是,他有一个最主要的缺点:如果你改变了如何组织和哪里放置视图组件(例如,改变组件的布局),你必须更新每一页--导致了额外的、禁止的改变。方案2成功的使视图组件可以重复使用,但是不能重复使用布局和模板逻辑。
方案3 Tiles insert


假如a.jsp如下:
<%@ taglib uri="/WEB-INF/tiles.tld" prefix="tiles" %>

<html>
<body>

<%-- include header --%>
<tiles:insert page="/header.jsp" flush="true"/>

a‘s body...
<p>

<%-- include footer --%>
<tiles:insert page="/footer.jsp" flush="true"/>

</body>
</html>

假如b.jsp如下:
<%@ taglib uri="/WEB-INF/tiles.tld" prefix="tiles" %>

<html>
<body>

<%-- include header --%>
<tiles:insert page="/header.jsp" flush="true"/>

b‘s body...
<p>

<%-- include footer --%>
<tiles:insert page="/footer.jsp" flush="true"/>

</body>
</html>


   方案3 使用Tiles insert 机制来代替JSP include 结构。使用Tiles insert 标签,你可将视图组件放置到恰当的位置。在其它所以方面,方案精确的镜像了JSP include方案(方案2),包括了优点和缺点。

方案4:Spiltting bodies


假如 a.jsp如下:
<%@ taglib uri="/WEB-INF/tiles.tld" prefix="tiles" %>

<html>
<body>

<%-- include header --%>
<tiles:insert page="/header.jsp" flush="true"/>

<%-- include body --%>
<tiles:insert page="aBody.jsp" flush="true"/>

<%-- include footer --%>
<tiles:insert page="/footer.jsp" flush="true"/>

</body>
</html>
假如b.jsp如下:
<%@ taglib uri="/WEB-INF/tiles.tld" prefix="tiles" %>

<html>
<body>

<%-- include header --%>
<tiles:insert page="/header.jsp" flush="true"/>

<%-- include body --%>
<tiles:insert page="bBody.jsp" flush="true"/>

<%-- include footer --%>
<tiles:insert page="/footer.jsp" flush="true"/>

</body>
</html>

方案4与Tiles insert方案有些小的不同。方案4将核心主体分隔成几个单独页面,如aBody.jsp and aBody.jsp.
假如 aBody.jsp如下:
a‘s body...
<p>
假如bBody.jsp如下:
b‘s body...
<p>


   方案4 的优点:他限制了body转变到各个页。同时,在其他地方他也让你能重复使用bodies,消除了重复的需要。因此,这个方案更加减少了通用视图组件于其他应用软件组件间的coupling 。产生和管理每个body组件引进了一个附加的复杂度。因为用其它的方案的话,每页依旧是他自己的布局。因此,没有过多的布局法则或模式。

方案5:Templating tiles
    使用Tiles的框架特性,我们可将下面的布局作为模板定义(来自下面所示的layout.jsp文件)。因为这个布局,所以用Tiles tag来插入标识符代替实际的视图组件。因此,对于所有组件而言,这页定义了一个可重复使用的布局:


<%@ taglib uri="/WEB-INF/tiles.tld" prefix="tiles" %>

<html>
<body>

    <%-- include header --%>
    <tiles:insert attribute="header"/>
    
    <%-- include body --%>
    <tiles:insert attribute="body"/>
    
    <%-- include footer --%>
    <tiles:insert attribute="footer"/>

</body>
</html>
其它的内容页,像a.jsp and b.jsp使用如上布局来放置组件。在实际的页面里,使用Tiles insert tag来插入布局。使用Tiles put tag可以为布局中特定的标识符指定实际的视图组件。
假如a.jsp如下:
<%@ taglib uri="/WEB-INF/tiles.tld" prefix="tiles" %>

<tiles:insert page="/layout.jsp" flush="true">
    <tiles:put name="header" value="/header.jsp"/>
    <tiles:put name="body" value="/aBody.jsp"/>
    <tiles:put name="footer" value="/footer.jsp"/>    
</tiles:insert>
假如b.jsp如下:
<%@ taglib uri="/WEB-INF/tiles.tld" prefix="tiles" %>

<tiles:insert page="/layout.jsp" flush="true">
    <tiles:put name="header" value="/header.jsp"/>
    <tiles:put name="body" value="/bBody.jsp"/>
    <tiles:put name="footer" value="/footer.jsp"/>    
</tiles:insert>


   方案5最突出的优势是他封装了布局原理图或结构方式,彻底的减少了通用视图组件和其他内容主体间的coupling。然而,他引入了其他布局页面增加了复杂性。首先,理解与执行模块就有困难。
方案6:Struts and Tiles
上面的layout.jsp布局页面,包含了组织组件的HTML and JSP code.内容页a.jsp and b.jsp不包含任何的HTML code;仅仅包括了Tiles tags来插入必需的组件。在一个XML文件中指定所有的内容页面会好一点吗?让我们来tileDefinitions.xml文件,指定其页面如下:


<?xml version="1.0" encoding="ISO-8859-1"?>
<component-definitions>
    <definition name="aDef" path="/layout.jsp">
        <put name="header" value="/header.jsp"/>
        <put name="footer" value="/footer.jsp"/>
        <put name="body" value="/aBody.jsp"/>
    </definition>
    <definition name="bDef" path="/layout.jsp">
        <put name="header" value="/header.jsp"/>
        <put name="footer" value="/footer.jsp"/>
        <put name="body" value="/bBody.jsp"/>
    </definition>
    <definition name="cDef" path="/layout.jsp">
        <put name="header" value="/header.jsp"/>
        <put name="footer" value="/footer.jsp"/>
        <put name="body" value="/cBody.jsp"/>
    </definition>
</component-definitions>
方案6把定义放置在XML文件中,消除了就像a.jsp and b.jsp一样的所有内容页。因像a.jsp and b.jsp这样的程序不再存在,我们怎样请求他?更重要的是,如何在tileDefinitions.xml文件中请求定义?
Struts and Tiles强大和协作的整体可被挽回。除了常规的配置参数以外,我们在web.xml文件中作为另一个参数指定配置文件的地址。如下所示。指定参数definitions_config使Struts能找到并知道关于Tiles 的定义。
<!-- Standard Action Servlet Configuration (with debugging) -->
  <servlet>
    <servlet-name>action</servlet-name>
<!--
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
-->
    <servlet-class>org.apache.struts.tiles.ActionComponentServlet</servlet-class>
    <init-param>
        <param-name>definitions-config</param-name>
        <param-value>/WEB-INF/tileDefinitions.xml</param-value>
    </init-param>
    ...
  </servlet>
我们现在定义一个Struts action,他将返回一个成功的在配置文件中指定的定义。Struts action DoFirst 是一个不运行的行为,如下所示:
package com.malani.struts.action;

import org.apache.struts.action.*;
import javax.servlet.http.*;

public class DoFirst extends Action {

    public ActionForward perform(
        ActionMapping aMapping,
        ActionForm aForm,
        HttpServletRequest aRequest,
        HttpServletResponse aResponse
    ) {
        return aMapping.findForward("success");
    }

}

你不能使用直接来自浏览器的定义,但是 你可以使用来自Struts的就像实际程序一样的一个。在struts-config.xml的文件中定义Struts actions如下:
<action path="/a"
            type="com.malani.struts.action.DoFirst"
    >
        <forward name="success" path="aDef"/>
    </action>

    <action path="/b"
            type="com.malani.struts.action.DoFirst"
    >
        <forward name="success" path="bDef"/>
    </action>

    <action path="/c"
            type="com.malani.struts.action.DoFirst"
    >
        <forward name="success" path="cDef"/>
    </action>
现在通过分别请求a.do and b.do actions来返回所需的程序。


   方案6的主要优点是他把所有的定义都统一到一个XML配置文件里。减少内容页很彻底的减少了总的页数。通过引进Struts,在另个方法上增加了复杂性。
方案7:Tiles inheritance
      在定义配置文件中,可观察到每页定义看起来是一样的。每个定义有三个组件,两个被固定为header and footer.强大的Tiles特性使得定义间有了继承性。因此,你可以定义一个基础定义,让原始定义从那个定义上继承来。原始的定义必须仅支持他们的单一部分。下面显示了带有定义间继承性的XML配置文件:


<?xml version="1.0" encoding="ISO-8859-1"?>
<component-definitions>
    <definition name="baseDef" path="/layout.jsp">
        <put name="header" value="/header.jsp"/>
        <put name="footer" value="/footer.jsp"/>
        <put name="body" value=""/>
    </definition>
    <definition name="aDef" extends="baseDef">
        <put name="body" value="/aBody.jsp"/>
    </definition>
    <definition name="bDef" extends="baseDef">
        <put name="body" value="/bBody.jsp"/>
    </definition>
    <definition name="cDef" extends="baseDef">
        <put name="body" value="/cBody.jsp"/>
    </definition>
</component-definitions>


     这个方案的优势是消除了配置文件理重复和多余的信息。总之,这个方案的优点和缺点与Struts and Tiles方案的一样。

方案总结
     如下表格总结了各个方案的评价指标方面的内容。我鼓励你增加其他的有创造性的方案,和其他重要的,像可延展性、可维护性、可操作性这样的评价指标。
各种解决方案的每项评估指标:

按此在新窗口浏览图片
     表格表明每个方案的复杂度是逐渐上升的。同时他也表明当你增加复杂度时,减少了编码复制,增加了设计控制的灵活性,减少了不相干视图组件的Coupling.页数最初是当视图组件分开时增加的,但当你在一个定义结构之中定义了更多页的时候,合并会发生。
那个方案是最好的?
最好的方案是要依据你项目所需和你在开发和维护一个web应用程序中的技能和知识。基本的方案太简单;我并不推荐他,因为他不支持好的软件工程师的实践经验的思路。如果你的web应用程序是复杂的,模板给你提供了一个很好的布局控制。因此你可能想研究和使用一个像Tiles一样的模板框架。如果你已经使用了Struts,接着你必须为一个强大的方案调节Tiles and Struts之间的协作。
Divine design
在这篇文章里,评价了不同的在HTML and JSPs中组织视图组件的方案。同时也研究了Struts and Tiles框架之间的协作性。这些策略和方案可以帮助你做一个有关于你的web应用程序的设计和结构的决定。
对审阅这篇文章的Max Cooper, Stephen Ditlinger, Dru Jensen, Phillip Lindsay, Roshni Malani, Danny Trieu, and Clare Zhang致以最诚挚的谢意。
资源:
方案1-7源代码:
http://www.javaworld.com/javaworld/jw-01-2002/tilestrut/1_basic.zip
http://www.javaworld.com/javaworld/jw-01-2002/tilestrut/2_JSPinclude.zip
http://www.javaworld.com/javaworld/jw-01-2002/tilestrut/3_tilesInsert.zip 
http://www.javaworld.com/javaworld/jw-01-2002/tilestrut/4_bodies.zip 
http://www.javaworld.com/javaworld/jw-01-2002/tilestrut/5_templatingTiles.zip
http://www.javaworld.com/javaworld/jw-01-2002/tilestrut/6_struts.zip 
http://www.javaworld.com/javaworld/jw-01-2002/tilestrut/7_inheritance.zip 
Tiles框架:
http://www.lifl.fr/~dumoulin/tiles/ 
posted on 2012-04-08 20:35  刺猬的温驯  阅读(424)  评论(1编辑  收藏  举报