Web显示层技术评估 -- 5.Scripted Template, 6, Template Manipulation, 7, Model Match, 8. 特性总表

Scripted Template

前面讲述了评估指标。下面分别各项技术进行单项说明。

(1) Scripted Template

HTMLServer Side Script混杂在一起的显示层技术。

包括JSP, Velocity, Freemarker, Taglib, Tapestry, XSL等。

 

Server Side的这些Scripted Template技术比较流行,耳闻能详。前面进行指标描述的时候,各种参数,也基本上涉及到了。就不具体展开进行单项的用法说明和特性分析。

JSP, Velocity, Freemarker的优势在于这些技术对用户后台Java代码侵入性非常低,这些Template都可以任意替换,而不影响用户后台Java代码。

 

下面讲述另外两类不是很常见的技术。

(2)Template Manipulation

Java代码直接操作Template(比如,HTML DOM)产生结果的显示层技术。

包括XMLC, JDynamiTe, Rife等。

 

(3) Model Match

Java代码负责提供符合显示层要求的Data Model,显示层框架本身把Data ModelTemplate进行匹配,产生结果。

包括Wicket, Fastm, DOMPlus, 等。

Template Manipulation

Java代码直接操作Template(比如,HTML DOM)产生结果的显示层技术。

包括XMLC, JDynamiTe, Rife等。

这类技术都具有良好的所见即所得特性。

(1)XMLC

http://xmlc.enhydra.org/

XMLC把一个HTML文件翻译成一个Java HTML DOM Class

比如,

<INPUT ID="nameInput">

<TITLE id="title">Hello, World</TITLE>

<SPAN id="para1">...</SPAN> ...

这些具有idHTML元素,在Java HTML DOM Class都产生了对应的方法。

HTMLElement getElementPara1()

public void setTextPara1(String text)

HTMLTitleElement getElementTitle()

HTMLInputElement getElementNameInput();

 

比如,<INPUT NAME="myName"> CLASS="class1 class2">

就产生了如下的Constant Fields.

   public static final String NAME_myName;

   public static final String CLASS_class1;

   public static final String CLASS_class2;

 

具体操作代码如下,

HTMLObject htmlObj = new HelloHTML();

// Construct head

HTMLHeadingElement head = htmlObj.createElement("h1");

Text headText = htmlObj.createText("Hello World");

head.appendChild(htmlTest);

 

// Construct anchor

HTMLAnchorElement anchor = htmlObj.createElement("a");

anchor.setHref("Welcome.po");

Text anchorText = htmlObj.createText("Welcome Page");

anchor.appendChild(anchorText);

 

// Replace contents of id-labeled node.

Element replace = htmlObj.getElementReplaceme();

Element parent = replace.getParent();

 

// Start with the last new child so we can use insertBefore

parent.replaceChild(anchor, replace);

parent.insertBefore(head, anchor);

 

可以看到,用户的Action Code里面充满了HTML DOM Node的添加删除操作。而且里面使用的代码都不是标准的DOM操作方法,而是代码生成的方法。代码侵入性非常强,如果要换成别的Template,比如JSP, velocity所有的代码都要作废。

当然XMLC产生的是一个DOM,后面还是可以接续XSL的。

 

一般来说,XML DOM操作只能针对完整的Node。一般需要替换整个Attribute,整个Text

对于,<a href=”http://www.mydomain.com/mymodule/{id}.html” 这类只需要替换某一部分的Attribute来说,处理起来就有点大而无当。这时候,XMLC引入了外部的Regular Expression Matcher等工具来处理这种情况。

另外有一个不常见的需求。动态替换Java Script代码的里面的某一部分。这时候,XMLC就完全无能为力了。或许也可以引入外来的Text Parser Engine,比如Velocity, Freemarker, Fastm, JDynamicTe等来做这件事情。

 

XMLC的主要问题还是空间效率问题。每次请求,用户需要产生一个Java DOM Class副本,进行操作。如果有多个用户访问同一个Page,那么就同时存在多个Java DOM Class副本。

当然里面的静态文本资源是共享的,我们看到上面的Java DOM Class里面,产生了很多String常数。

但是DOM Node结构本身的尺寸就比较大。即使采用了一些优化简化的DOM Parser,去除了用不到的结构,整个尺寸还是比较大。

(2) JDynamiTe

http://jdynamite.sourceforge.net/doc/jdynamite.html

 

JDynamiTePHPLib Template的移植。采用XML Comment的方式标记动态结构块。

我们来看一个典型的两层循环的例子。<tr><td>

 

<table border=1>
<!-- BEGIN DYNAMIC : myBigRow -->
<tr>

<!-- BEGIN DYNAMIC : colX -->
<td>{VALUE_X}</td>
<!-- END DYNAMIC : colX -->

<!-- BEGIN DYNAMIC : colY -->
<td bgcolor="#33CCFF">{VALUE_Y}</td>
<!-- END DYNAMIC : colY -->

</tr>
<!-- END DYNAMIC : myBigRow -->
</table>

 

对应的代码是,

import cb.jdynamite.*;

 

dynamiTe=new JDynamiTe();

dynamiTe.setInput(application.getRealPath("cbtemplate/testTemplate.html"))

    // Second table with nested Dynamic Element
    for (int row = 0; row < 5; row++) {
        // first group of columns
        // 4) Use "setDynElemValue" to set or reset the value of a Dynamic Element
        dynamiTe.setDynElemValue("colX", ""); // reset for each row
        for (int col = 0; col < 3; col++) {
            dynamiTe.setVariable("VALUE_X", "line_" + row + ",col_" + col);
            dynamiTe.parseDynElem("colX"); // add a column
        }
        // second group of columns
        dynamiTe.setDynElemValue("colY", ""); // reset for each row
        for (int col = 3; col < 5; col++) {
            dynamiTe.setVariable("VALUE_Y", "line_" + row + ",col(BIS)_" + col);
            dynamiTe.parseDynElem("colY"); // add a column
        }
        dynamiTe.parseDynElem("myBigRow"); // add a row
    }

    // 5) Use "parse" to finaly get the value of your Dynamic Template Document
    dynamiTe.parse();

    out.println(dynamiTe.toString()); // send HTML page

 

我们看到,Template本身操作贯穿程序的始终。

setDynElemValue, setVariable, parseDynElem, parse都是template Class本身的方法,类似于XML DOM Node的添加删除修改。

我们看到这类DOM Manipulator的代码侵入性非常强,用了之后,如果要换别的Template,比如JSP, velocity,这段代码完全作废。

(3) Rife

http://rifers.org/

类似于JDynamiTeRife也采用自定义动态块标签。

下面是一个典型的例子。递归显示一个Data Tree。下面只是核心片断。如果对整个例子感兴趣,可以去Rife的网站查看SampleTutorial

这是一段Tree Template

<body>

${v level/}

${b level}

  <ul>${v nodes/}</ul>

${/b}

${b node}

  <li>${v title/}${v level/}</li>

${/b}

</body>

 

对应的Java代码操作Template Node,输出Data Tree

 

import com.uwyn.rife.engine.Element;

import com.uwyn.rife.template.InternalValue;

import com.uwyn.rife.template.Template;

 

// obtain an instance of the template that will output the tree

Template template = getHtmlTemplate("tutorial.recursion");

// obtain a new internal value to construct a collection

// of sibling child nodes in the local scope

InternalValue   nodes = template.createInternalValue();

// set the child's title value

template.setValue("title", encodeHtml(child.getTitle()));

// and append it to the local internal value

nodes.appendBlock("node");

// set the level value which includes the sibling nodes in the

// same level

template.setBlock("level", "level");

 

我们看到,template的操作代码贯穿整个程序的始终。getHtmlTemplate, createInternalValue, setValue, appendBlock, setBlock。非常类似于上面JDynamiTe的用法。

JDynamiTe显示Data Tree的过程也是大致如此。XMLC也是如此。

Rife同样具有JDynamiTeXMLC的代码侵入性强的缺点。如果需要换别的Template技术,比如JSP, velocity,整个代码都要做废。

Model Match

Java代码负责提供符合显示层要求的Data Model,显示层框架本身把Data ModelTemplate进行匹配,产生结果。

包括Wicket, Fastm, DOMPlus, 等。

这类技术都具有良好的所见即所得特性。

(1) Wicket

http://wicket.sourceforge.net/

Wicket类似于Tapstry,采用HTML自定义Attribute作为自定义标签。

这段是Rife的一个典型的循环的例子。wicket:id一个标签,几乎可以满足任何需求。有兴趣的读者可以去Wicket网站查看完整的Sample。这里只有核心片断。毕竟,本文不是一部Wicket教程。

 

<html>

<body>

  <form wicket:id = "commentForm">

    Add your comment here:

    <p>

    <textarea wicket:id = "text">This is a comment</textarea>

    <p>

    <input type = "submit" value = "Submit"/>

  </form>

  <p>

  <span wicket:id = "comments">

    <p>

           <span wicket:id = "date"> 1/1/2004 </span><br>

           <span wicket:id = "text">Comment text goes here.</span>

       </p>

  </span>

  <wicket:remove>

    <p>

           1/2/2004 <br/>

           More comment text here.

    </p>

  </wicket:remove>

</body>

</html>                       

 

我们可以看到,Template非常干净。只有少数的自定义attribute, (and tag)

对应的Java代码如下。

 

import wicket.markup.html.WebPage;

import wicket.markup.html.basic.Label;

import wicket.markup.html.basic.MultiLineLabel;

import wicket.markup.html.form.Form;

import wicket.markup.html.form.TextArea;

import wicket.markup.html.list.ListItem;

import wicket.markup.html.list.ListView;

import wicket.model.PropertyModel;

 

public final class GuestBook extends WebPage

{

       /** Use a Vector, as it is synchronized. */

       private static final List commentList = new Vector();

       private final ListView commentListView;

 

       public GuestBook()

       {

              add(new CommentForm("commentForm"));

              add(commentListView = new ListView("comments", commentList)

              {

                     public void populateItem(final ListItem listItem)

                     {

                            final Comment comment = (Comment)listItem.getModelObject();

                            listItem.add(new Label("date", comment.date.toString()));

                            listItem.add(new MultiLineLabel("text", comment.text));

                     }

              });

       }

}

 

我们看到,Wicket的代码,相当干净利索,虽然写法上使用了匿名内部类。没有任何Template本身的操作。只是需要提供一个框架需要的View ModelListViewMultiLineLabelLabel

WicketPropertyModel能够用来包装一个POJO。比如,一段HTML Template

Stock of IBM: <span wicket:id="stockIBM">some value</span>

 

对应的Java代码是

import wicket.model.PropertyModel;

 

public PojoStockQuotePage()

{

    StockQuote quote = new StockQuote("IBM");

    add(new Label("stockIBM", new PropertyModel(quote, "quote"));

}

 

我们看到,Wicket的代码结构非常像Swing。只需要对应HTML UI Tag提供一份View Model就可以。操作起来实在是方便。而且HTML Tag里面只需要添加 Wicket:id这样的自定义Attribute,就可以同时表达动态层次块和变量部分(其实Rife也是如此)。

 

当需要换Template的时候,比如JSP, Velocity, FreemarkerTaglib等,Wicket提供的View Model还是可以使用的。

Wicket的一个不足之处是,代码需要使用框架自定义的HTML View Model。这也可能是一个优点,能够帮助用户清楚地理解,代码和HTML Template之间的对应关系。

 

从严格意义上来说,比起Taglib, Tapestry来说, 只有Wicket, Echo这样的框架才是真正意义上的组件框架。而且,Wicket相对于Echo的优势如此明显,这里就不多说了。不然就跑题了。总之,Wicket是一个非常值得关注的框架。

(2) Fastm

http://fastm.dev.java.net/servlets/ProjectDocumentList

Fastm的思路相当于JDynamiTe, Wicket的思路组合。

Fastm = JDynamiTe + Wicket

Fastm采用自定义标签来标记动态Block,然后类似于JSTL, Velocity, Freemarker, Tapestry那样,接受一个POJO作为Model,并采用OGNL Style(同时也接受XPath Style)的方式对数据进行寻址。

Fastm的公式很简单,Fastm Template + Model = Result

这个ModelPOJO。可以是Java Bean, Map, DOM Node等任何Object

 

我们来看一段典型的Tree Data递归显示的例子。

这段是HTML Template片断。

 

class name: <input type="text" value="{name}">

<!-- BEGIN DYNAMIC: @children -->

<ul>

<!-- BEGIN DYNAMIC: children -->

  <li>

    class name: <input type="text" value="{name}">

    <!-- BEGIN DYNAMIC: @children -->

    <ul>

    <!-- BEGIN DYNAMIC: children -->

    <!-- END DYNAMIC: children -->

    </ul>

    <!-- END DYNAMIC: @children -->

  </li>

<!-- END DYNAMIC: children -->

</ul>

<!-- END DYNAMIC: @children -->

 

上面的@children需要特别说明一下,意思是检测当前Model是否具有children这个property,如果具有,那么向下展开,否则就跳过去。这样的话,如果没有children的话,多余的<ul> tag就不需要打印出来了。虽然空<ul>tag并不影响显示。

 

对应的Java代码只需要提供一个POJO作为Tree Data

 

    public Object makeModel(){

       Map a = new HashMap();

       a.put("name", "A");

       List aChildren = new ArrayList();

       a.put("children", aChildren);

       {

           Map a1 = new HashMap();

           a1.put("name", "A1");

           List a1Children = new ArrayList();

           a1.put("children", a1Children);

           {

              Map a11 = new HashMap();

              a11.put("name", "A1-1");

              a1Children.add(a11);

 

              Map a12 = new HashMap();

              a12.put("name", "A1-2");

              a1Children.add(a12);

           }

           aChildren.add(a1);

       }

       return a;

    }

 

这段代码采用了Map作为Model, 也可以采用Java Bean, XML DOM Node等任何Object。所以,当然可以提供一个XML文件作为Model

 

看起来FastmJSP, Freemarker, VelocityJSTL一样,对View Model没有什么特殊要求。POJO就可以。基本上就是如此。

且慢,FastmModel还是有特殊要求的。类似于WicketFastm也没有逻辑标签,Fastm也利用数据来表示条件、循环等逻辑分支。遇到Collection, Array等数据类型,就自动把动态块展开。如果不显示某一块,那么就提供一个空数据。

这就是Fastm所需要的所有约定。看起来很简单,真正满足这个约定也不难。但是,某些特殊的情况下,为了满足这个约定,需要后台的用户代码做一些比JSP, Velocity, Freemarker要求的更多的Model组装工作。

Fastm的另一个问题是,ModelViewTemplate)之间的映射关系不是很明了。虽然比JDynamiTeRife等容易明白多了,但是比Wicket还是差一些。因为Fastm没有自定义View Model类型,需要用户自己掌握Bean, Map层次和Template层次之间的正确对应。

(3) DOMPlus

http://fastm.dev.java.net/servlets/ProjectDocumentList

DOMPlusFastm的思路在XML DOM领域的扩展。

如果说,Fastm = JDynamiTe + Wicket;那么,DOMPlus = XMLC + Wicket

DOMPlusXMLCWicket思路的组合。

如果说,Fastm的公式是,Fastm Template + Model = Result

那么,DOMPlus的公式是,DOM + Model = DOM or SAX

这个ModelPOJO。可以是Java Bean, Map, DOM Node等任何Object

一个很有趣的现象就是 DOM + DOM = DOM

没错,就是如此。

 

DOMPlus采用自定义DOM Attribute来标记动态Element, 动态Attribute, 动态Text

我们来看一个典型的Tree Data 递归显示的例子。

对应的HTML DOM片断是。

 

  <li nodeTarget = "child">

    class name: <input type="text" attributesTarget="value=@name" />

    <ul>

    <li nodeTarget = "child" />

    </ul>

  </li>

 

nodeTarget表示可能被重复多次的动态ElementattributesTarget表示需要替换的attribute。这里数据寻址方式采用的是XPath@name表示DOM Nodename attribute

DOMPlus的自定义标签只有3个,nodeTarget, attributeTarget, textTarget

 

对应的XML Tree Data 是,

 

<data name="topClass">

<child name="A">

  <child name="A-1">

    <child name="A-1-1"/>

  </child>

  <child name="A-2">

    <child name="A-2-2">

      <child name="A- 2-2-3 " />

    </child>

  </child>

  <child name="A-3"/>

</child>

<child name="B">

  <child name="B-1" />

</child>

<child name="C" />

</data>

 

这两个DOM一匹配,就产生了结果XML,是一棵显示在HMTL List里面Tree

 

DOMPlusTemplate更加干净,几乎接近于XMLCPure HTML DOM,等同于Wicket

而且DOMPlus并没有XMLC的空间问题。DOMPlus只保留一份DOM在内存中,每次请求来的时候,DOMPlus根据数据匹配产生SAX Event,直接写入Response

 

DOMPlus的匹配引擎很类似于XSL的功能,同样是用XML格式的模板文件处理XML数据。而且两者都同样是递归处理引擎。

所不同的是DOMPlus Template能够在浏览器中正确显示,而且表达结构的自定义属性非常简单,只有3个(Fastm2个)。

 

DOMPlus的问题和Fastm一样,数据层次和模板层次之间的关系,一定要非常清楚,而不是像JSP, Velocity, Freemarker那样把数据抓过来就可以用。

另外的问题就是,处理的最小单位是XML NodeXML Node里面的text的部分替换就无能为力了。比如 <a href=”http://www.domain.com/module/{id}.html”

XMLC的解决方法一样,可以引入外面的文本解析器。用来处理XML Node鞭长莫及的地方,比如,JavaScript代码的内部的动态替换部分。

Regular ExpressionVelocity, Freemarker, Fastm, JDydanamiTe,等任何能够脱离Web环境的通用文本解析工具都可以。JSP, Taglib, Tapestry, Wicket等无法脱离Web环境而存在,肯定不行。

 

------

注:

FastmDOMPlus是我的作品,轻量的Template 匹配引擎。这两项技术本身只是单项的Template显示技术,不是一个完整的web整体解决方案。

特性总表

(1) Host Language Consistency宿主语言一致性

(2)Template Purity 模板纯净度

(3)Template Tidiness 模板整洁度

(4) Replacement Flexibility 替换灵活度

(5)WYIWYG 所见即所得

(6)Action Code Purity 用户代码纯净度

(7) Infrastructure Code Purity 基架代码纯净度

(8)动态Include

(9)Recursive Display of Tree Data树型数据的递归显示

(10) Space Efficiency空间效率

(11) Mapping Explicitness 映射关系明显度

(12) Display Logic Reusability 显示逻辑重用度

 

限于空间,下面的分数表采用索引数字来代表特性。横向是特性,纵向是技术。

下面的表格中没有写Freemarker,只写了Velocity,这两项技术比较类似。各项参数也大致接近。当然,各自的Fans都能够看到很深很细的使用细节,宏定义之类的。

 

指标索引

1

2

3

4

5

6

7

8

9

10

11

12

Scripted Template

Velocity

2

0

0

10

0-5

10

10

10

0

10

10

0

JSP

10

0

0

10

0

10

10

10

0

10

10

0

Taglib

0

0

10

6

0-5

2 - 8

0

0

4

6

6

0

Tapestry

0

0

10

6

7

4

0

0

4

10

4

0

XSL

10

0

10

6

0

10

10

10

10

10

5

0

Template Manipulation

XMLC

5

10

10

6

10

0

10

10

10

0

7

4

JDynamiTe

5

8

5

10

8

0

10

10

10

3

3

5

Rife

5

3-7

5

10

0-7

0

10

10

10

3

3

5

Model Match

Wicket

5

9

8

6

9

2

0

0

4

10

8

4

Fastm

5

7

5

10

8

6

10

10

10

10

3

8

DOMPlus

5

9

8

6

10

6

10

10

10

6

3

8

 

 

下面我们大致看一下Browser SideAjax)的情况。

 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值