[FreeMarker 2.3.20] Part I 关于模版设计的介绍 ~值和类型~ 类型

简介


FreeMarker 中支持的类型有这些:

  • Scalars :
    • String
    • Number
    • Boolean
    • Date
  • 容器:
    • Hash
    • Sequence
    • Collection
  • 子程序 (Subroutines) :
    • 方法和函数
    • 用户自定义指令 (directives)
  • 杂项 (Miscellaneous) / 很少使用:
    • Node


Scalars


这些在职类型中是比较基础、简单的,包括:

  • String :简单的文本。如果你不想用来自 data-model 中的变量,而是直接将字符串直接插入到模板中,那么就把那些文本放到引号中即可,比如,"green.mouse" or  "mouse(更多的语法细节可以在后边的章节中看到)
  • Number :说一件商品的价格。无论是整数 (whole number) 或非整数 (non-whole number),他们之间是没有区别的,在 FreeMarker 中只有一种数据类型就是 number. 比如,3/2只会是1.5,而不会出现1,就像是使用计算器一样。当想要在模板中插入数值类型时,那么你就要这样写了:150 or -90.05 or 0.001 (更多关于语法方面的细节将会在后边章节介绍)
  • Boolean : 一个 boolean 类型的值代表了一个逻辑 true or false (yes or no). 比如说,一个用户是否有登陆。最典型的就是在 if 的条件部分使用 booleans,如, <#if loggedIn > .. </#if>or<#if price == 0> .. </#if>; 在后一个例子中 price == 0 的结果就是 boolean 类型的。在模板文件中你可以用保留字 trueorfalse 来指定 boolean 值。
  • Date : 存储日期/时间相关的数据。它有三种变种:
    • 以天为精确值的类型 (通常就是用日期 (date)来表示),比如 2003年4月4日
    • 一天的时间 time (没有日期部分),比如说 10:19:18 PM, 这种类型的数据是一毫秒为精度的
    • date-time (有时也会称为"time stamp") ,比如说 2003年4月4日, 10:19:18 PM. 同样一天的时间那部分仍然是以毫秒为单位的

不幸的是,由于 Java 平台的限制,有时 FreeMarker 无法决定一个 date 类型的数据无法决定会使用哪一部分 (可能是 date-time or time, etc.) 对于这个问题的解决方案是一个高级主题,会在后续的章节中讨论。


需要记住的是 FreeMarker 把 strings, numbers, booleans 分别对待,因此 "150"150是完全不同的。一个数类型持有数值, 一个布尔类型持有逻辑 true or false。 一个 string 类型持有任意的字符序列集合。


容器


容器这些值的主要目的是存储别的变量;他们只是容器。那些被持有的变量通常就是指子变量。容器的类型有以下几类:

  • Hash :它的每一个字变量都一个与其关联的唯一的查询名称。它的名称是一个无约束的字符串。在 hash 里边的变量是没有定义一个先后顺序的。因此,就没有第一个子变量,第二个子变量,等等,这些说法。这些子变量只能够通过名称来访问。
  • Sequence : 对应其每一个子变量都有一个关联的数,下标。第一个子变量与 0 相关联,第二个和 1 关联,以此类推。它的子变量是有序的。这些数通常被称为子变量的索引,下标。通常序列中值的存储是比较紧凑的,比如说,一般直到最后一个子变量的下标都是关联着一个子变量的,但是这个并不是严格要求的,而且子变量的值也并不需要是一样的。
  • Collection : 从一个模板作者的角度来看,是一个严格的序列,你不能访问它的大小,也不能通过下标来遍历子变量,但是它们仍然是可以通过 list 指令将其列出来。


要注意的是因为一个值可能会拥有多种类型,它可能同时是一个 hash 类型和一个 sequence类型,也就是说它的值支持以下标为基础的访问和以名称为依据的访问。但是呢,还是有个例外的就是,一个容器类型只可能是序列或是集合,是不可能同时都属于。


由于存储在 hashes, sequences, collections 中的值可以是任何值,因此,也就有可能是一个 hash, sequence, collection 类型的值。在这中存储方式下你就能够建立任意深度的存储结构。


data-model 本身 (准确的说是 root ) 是一 hash 类型。


子程序


方法和函数


一个变量的值是用来计算其它值的方法或函数,那么计算出来的值会被所给的参数所影响。


对程序员说的话 (for programmer types): 对于面向过程的程序语言来说,方法/函数是最经典的值。这意味着方法/函数可能是参数也可能是别的方法/函数的值,你可以将他们赋值给变量,等等。


假设程序将可以用来计算平均值的方法变量 avg 放入了 data-model,当你访问avg时,附带了参数 3 和 5 ,那么你将会得到值 4.


关于方法的使用会在后边进行解释,不过下边这个例子大概能帮助理解什么是方法:

The average of 3 and 5 is: ${avg(3, 5)}
The average of 6 and 10 and 20 is: ${avg(6, 10, 20)}
The average of the price of a python and an elephant is:
${avg(animals.python.price, animals.elephant.price)}  

这会有如下输出:

The average of 3 and 5 is: 4
The average of 6 and 10 and 20 is: 12
The average of the price of a python and an elephant is:
4999.5  

那么方法和函数之间有什么区别呢?就如模板作者所考虑的,没有。当然也并不是没有点差别,因为方法一般是来自 data-model的 (它们主要是反映了 Java 对象的方法),而函数时定义在模板里边的 (用函数指令定义的——一个高级主题),不过两者的使用方法是相同的。


自定义指令


这种类型的值可以被用作用户自定义指令 (换句话说就是指 FreeMarker tag)。一个自定义指令是一个子程序,有点像可重复使用的模板片段。不过这个是一个高级主题,我们同样会在后边其自己的章节中介绍。

对程序员说的话 (for programmer types): 自定义指令 (比如宏,macros),也是经典的值类型,就如同方法和函数一样的经典类型。

这里只是对自定义指令有个了解 (所以如果你不明白的可以先忽略掉这部分),假设我们有个变量 box, 它是一个用来输出漂亮且带有标题条和信息的 HTML 消息框的自定义指令。box变量可以像下边的例子一样在模板中使用:

<@box title="Attention!">
  Too much copy-pasting may leads to
  maintenance headaches.
</@box>  

方法和函数 VS 自定义指令


这个也是对高级用户而言的 (如果你不明白也可以忽略掉这部分)。 当你面对要实现某个功能时,在方法/函数和自定义指令间的选择是比较困难的。一般规则 (the rule of thumb):当是以下情况的时候使用自定义指令来代替方法/函数:

  • ...输出(返回值) 是标记(HTML, XML, etc). 主要的原因是函数的结果是那些能够自动进行 XML 转义(XML-escaping)[主要是由于 ${...} 的缘故]的值 (subject),但是自定义指令却不会 (这个主要是由于<@...>, 它的值被认为是标记了,因此就不会被转义)。
  • ...它的作用是产生 side-effect (比如说更改一个全局变量或一个静态变量) 而不是返回值。比如说,它的目的是在服务器 log 中加入一对键值。(事实上自定义指顶也不会有返回值的,不过某些反馈通过设置非本地变量仍然是可能的)
  • ...它会产生流控制 (比如像是 list, if 指令所做的). 这种功能对于方法/函数是不能实现的。

不识别 FreeMarker 的 Java 对象的方法在模板里边一般是以方法出现的,而不会在意它是 Java 方法这个事。 (The Java methods of FreeMarker-unaware Java objects are normally visible as methods in templates, regardless of the nature of the Java methods, 这个感觉比较难翻译,求赐教:))也就是说,这个没有选择的余地.


杂项


节点


在一个树形结构中一个结点变量代表一个结点,它最多是被用在解析 XML 过程中,这也是个高级,特别的主题。


仍然,这里我们为高级用户做一个总结:一个结点就如同序列一样能够存储其他的节点,这些节点被称作子结点。一个结点会存储其容器结点的引用,这个节点被称作父亲结点。一个结点主要的信息就是它的拓扑信息,其他存储下来的数据则是能够有其他用途的,这样的值可以有多种类型的。(这里应该是指除了用来表示结点与结点之间的关联信息外的其他信息,比如说权重什么的。)比如一个值可能是一个结点和一个数,这个数刚好就代表负载。出去拓扑信息外,一个结点也可以附带一些源信息:结点名称、节点类型 (string),结点的命名空间 (string). 比如说在一个 XHTML 文档中有个结点元素用 h1表示,那么它的名字可以是"h1", 它的结点类型可以是 "element", 它的命名空间可能是"http://www.w3.org/1999/xhtml".当然这个就由 data-model 的设计者来决定这些元信息究竟是代表什么意思以及它们是否会被是使用。关于便利拓扑结构和源信息的方法会在后边的章节里讲到 (那么现在不明白的话也没关系)

发布了13 篇原创文章 · 获赞 5 · 访问量 6万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章