1、在 模 板 引 擎 中 强 制 模 型 -视 图 严 格 分 离被 推荐 为 最佳 论 文Terence Parr旧 金山大学译 注 :部 分 翻 译 对 照以及 说 明 ,参 考 文 章 最 后 的描述Translated by: Richie摘 要:每一 位富有经 验 的 web 应 用 开 发 者 都 主 张 :应 当将 业 务 逻 辑 和 显 示分离 。然 而 几乎 所 有的模 板 引 擎 都 允许违 背 这 一 分 离 原 则 ,这 极 大 的 推 动 了 HTML 模 板 引 擎 的 发 展 。这 种 情 况 主 要 是 由 于 对 分 离缺乏 形 式的定 义 ,以及 担 心 强
2、制 分 离 将消弱 模 板 的 生 成 能 力 (generational power。我 将 阐 述 严 格的分离 是 非 常 有 用 的 设计 原 则 ,通 过 提 供 有 效 的 模 板引擎能 够强 制 分 离 。我 使 用 StringTemplate 引擎 作为示 例, 用 来 建立 jG 和 其 它 的商 业 网 站 ,并 解 决 一些重要的生 成 问 题 。我 的 目的是 使 对 模 板 引 擎的研 究 形式化 ,因此 提 供 一 个 通 用的 术语 ,这 个 术 语 是 一 种 对 模 板 生成能 力 分类 的 方 法 ,是 对 形 式 语 言理 论 (formal langu
3、age theory)感 兴趣的 补 充 。类似 Chomsky 的 13 型 语 法 分 类 , 我把限 制 性 模 板( restricted template)分 成 三 种 类 型 ,并 且形式的定 义 分 离 ,包 括 实 现 分离的 规则 。因 为 这 篇 论 文 提 供 了 一 个 清 晰 的 模 型 -视 图 分 离 定 义 ,模 板 引 擎 设 计 者 也 许 不 再 盲 目 的 主 张 强 制 分 离 概 念。 此 外 ,给 出 理 论论 点 和 经 验 证 明 ,程 序 员 不再有借口混 杂 (entangle)模 型和 视 图 。分 类 和 主 题 描 述 :D.3.4
4、 程 序 语 言 :处 理 机 -代 码 生成; D2.11 软 件 工 程 :软 件架构 -面 向 领 域 的 架 构 ,模 式 ,语 言 ;D1.1 编 程技 术 :应 用 (功能) 编 程常 规 项 : 语 言关 键 词 :模板 引 擎 ,Web应 用 ,模 型 -视 图 -控制器1. 简 介 对 动态 生成 web页 面 的需求 ,例 如 A上的 图 书 显 示 页 面 ,导 致 众 多 模板引 擎 的开 发 趋 向 于 使 web应 用 开 发 简 单 化 ,提高 扩 展性, 降 低 维 护 成 本 ,允 许编码 和 HTML的开 发 并 行 。这 种 诱 人 的 好 处 促 进 了
5、 模 板 引擎 的 发 展 ,这 完全来自一个原 则 :将 页 面 的 业务逻辑 声 明 (specification)和 数据 处 理 声 明 ,与 页 面 怎 样显 示 这 些 信 息 的 声 明 分离。通 过 隔 离 封 装 的 声 明 ,模板 引 擎 促 进 了 组 件 复 用 ,可插 拔 的 站 点 外 观 ,通 用 组 件的单 点 变 化 (single-points-of-change),良 好 的 系 统 清晰度。 我 曾 经 跟很 多 经 验 丰 富 的程序 员 讨论过 分 离 原 则 ,调 查 过 很多通用的模板 引 擎 ,这 些 模 板 引 擎使用 各 种语 言 开 发
6、,包 括 Java、C和 Perl。毫 不 意外 ,程 序 员赞 成 逻 辑 与 显 示分离 是 一种理 想 的 原 则 。然 而 实 际 上 ,程序 员 和 引 擎 开 发 者 不太原 意 强 制 分 离 ,他 们 担心 满 足 这 个原 则 会 损 失 生 成能力 ,导 致 某 个 关 键 页 面 无法生成。 相反 ,他 们 鼓 励 而 不是 强 制要求 这 一 原 则 ,给 自己留 一 个 退 路 ,避 免 不 完 善的 页 面生成 能 力 。很 不 幸 ,在 最 终 期 限 的 压 力 下 ,只 要 可 能 ,程 序 员 会 经 常 使 用 这 个 退 路 作 为 权 宜 之 计 ,导
7、 致 逻 辑 和 显 示 混 杂 。一 个 负责 公 司 服 务 器 数 据模型的程序 员 告 诉 我 ,离 最 终 期限他只 有 3天 多 时 间 ,但 如 果 强 制他 们 的 程 序员 采 用 分 离 原 则 ,修改那些受影 响 的 多 语 言 页 面 显 示需 要 10天 。为 将 来 的 维护 考 虑 做正 确 的 事 情 ,他 可能面 临 被 解 雇 的 风 险 ,或者他 可 以 通 过 数 据 模 型 将 新 的 HTML输 出 到 页 面 ,从 而 保住他 的 工作, 将 这 种 混 乱 留 给 以后 或 者 其 它 程 序 员 。另 一 种更普 遍 的 情 形 是 作 为 一
8、 个 捷径 ,程序 员 将 业 务 逻 辑 嵌 入他 们 的模板 中 ,避 免 更 新数据模型 。提 供 完全 图 灵 机 式 (Turing-Complete)的 模板 编 程 语 言 ,程序 员 就 趋 向 于 直 接 在 模 板中需 要 的 地 方 添 加 业 务 逻 辑 , 而不 是 在 数 据 模 型 中完成,从模 型 中 解 耦 视 图 。例如几乎每 个 模 板 引 擎 的 文档都有描述 ,如 何 根 据 用 户权 限 改 变 显 示 ,不 是 简 单 的向 模 型 询 问 用 户 是 否 特 殊 ,而 是 在模 板 中 编 码 逻 辑 来确定用 户 是 否 特 殊。如 果 这 个
9、特殊的 定 义 改 变 了 ,可能系 统 中的 每 个 模 板 都 不 得不修改,或 者 程 序 员 可 能 忘了某个模 板 ,给 系 统 引 入 一个将 来随 时 可 能 出 现 的 缺陷。通常 这 种 捷 径 很 快 导 致完全 混 杂 的 声 明 (entangled specification)。理 想 是否可 能 ? 就 是 说 我 们 是 否 可以 强 制 分 离 却 又 不消弱 模 板引擎 的 生 成 能 力 ?限制 性 引擎 导 致 一 些版 权归 原作者所有WWW2004, May 1720, 2004, New York, New York, USA. ACM 158113
10、844X/04/0005.页 面 无 法 处 理 ? 通 常理 论 跟 实 践 是 不 一 致 的 。理 论 表明一个 完 全 图 灵 机 式 的模板引擎能 够 生 成 任 何 页 面,并 且比 限 制 性 模 板 引 擎更 强 大。 实 际 中 我 建 立 web服 务 器 的 经 验 ,例 如 jG(11万 行 ),提供 了 有 力 的 证 据, 表 明 程 序 员 实 际 上在 这 两 个 方 面 可 以 获 得 最好的 效 果 :分 离 和 足 够 的 生成能力。 过 去 五 年 我 在 构 建站点 和打 造 我 的 StringTemplate模 板引擎 过 程中 ,实 际 上 是
11、进 行 了 一 项 软 件 设计 的 试 验 ,在 我 的 模 板中 严 格 的 避 免 逻辑 和 运 算 (译 注 :对 上面例子 的 解 释 ,完 全 图 灵机式的模 板 引 擎 =自由 模 板 引 擎 ,例 如 JSP、ASP;自 由 模 板引 擎 理 论 上 强 大, 但 实 际 中 因 模 型 视 图 混 杂 而 问题 很多; 限 制 性 引 擎 理 论 上能 力 弱 ,但 作 者 的 实 际 经验 表 明能够 在 强 制 分离 和足够的 生 成能 力 两 方 面获 得最好的 效 果) 。尽 管 这些 微不足道 的 证据 还 不 能 证明 我的限 制性 引 擎 合 格 ,另 一方面就
12、算 绝 大 部 分 模 板 引 擎的能力通 过 泰 勒 级 数 (Taylor series)接近 sin(x)(译注:意 指 100分 ? ),我 不 相 信一个 设 计 师 真 的 需 要。关 键 是不提 供 允 许 违 背 分离的 结 构 ,而 使模板 引 擎 具 备 足 够 的生成能力 。在研究 了 我 的 站 点 上几百 个 模 板文 件 之 后,我 确 定只需 要 四 种 模 板 结 构 :属 性引用 (attribute references),包 括 判 断 属性是 否 存 在 的 条 件 模板( conditional template),递归 模板引 用 (recursiv
13、e template references),以及最 重 要 的 类 似 lambda函数 和 LISP的 map这 样 对 多 值 属性操 作 的 模 板 应 用 (template application)。到 此 我提出 的 观 点 是 严 格的 强 制 分离不 仅 是 有 用 的 设计 原 则 ,而且通 过 选 择 正 确 的 模 板 结 构 实际 上 是 可行的 。第 2节 描 述 模 板 引擎怎 样 从 之 前 的 策 略 发 展 到目前 解 决 各 种 实 际 设 计 问 题 ,第 3节 明 确的 阐 述 分 离 的 好 处 ,第 4节 说 明 模型 -视 图 -控 制 器 模
14、式怎 样 自 然的运 用 到 服 务 器 端 设 计 ,以 及 怎 样 实 现 逻 辑 (模 型 )和 显 示 (视 图 )的 分 离 。第 5和第 6节 正 式 的 定 义 模板和三 个 限 制 性 模 板 类 型 ,第 7和 第 8节 定 义 严 格的模型 -视 图 分 离 原 则 , 以及 怎 样 避 免 混 杂 ,最后第 9节 演示 StringTemplate引 擎 解 决 一 些 重 要 的 HTML生成任 务 。2. 模 板引 擎 的 发 展 生 成 动 态 页 面意 味 着 服 务 器 不 再 将 URL映射 到 磁 盘 HTML文 件 ,取 而 代 之的是映射到 一 堆 代
15、码 ,这 些代码丢 出 正 确 的 HTML,包 括 内 容 和 相 关的 显 示 指令 。在 这 我 将 描 述 Java引 擎 的 发 展 ,这 些 概 念 同 样 适 用 于 Perl、VisualBasic等 。Java从Servl ets 15开 始 支 持 服 务 器 开 发 ,它 调 用那些响 应 HTTP GET和 POST命 令 的 方 法 ,通 过 输 出 语 句生成 HTML。例如 下 面 是 一 个 简单 servlet的 主要 处 理 ,它生 成 一 个 页 面 ,向 URL参数 传 入 的 姓 名 问 候 “hello”。out.println(“); out.pr
16、intln(“); out.println(“Servlet test“);String name = request.getParameter(“name“); out.println(“Hello, “+name+“.“); out.println(“); out.println(“);问 题 在于在 Java代 码 中声明 HTML很 烦 杂 ,是一种 错误导 向 ,HTML无 法 由界面 设 计 师 编 写 。为 了 改 善这 种 状 况 ,程序 员 可 以 尝 试 将 普 通 的 HTML输 出 元 素 提 取 成 Java呈 现 对 象 (Java rendering object
17、s),例 如 Table、BulletList,但 终 究 还 是 在 servlets中 嵌入 HTML方 式。接下 来的发展 阶 段就 是 引入了JS P 6,大致 看 来 是 前 进 了 一 大 步 。JSP文 件本 质 上就是 servlets,将 Java代 码 嵌 入 在 HTML文件中 ,由 服 务 器 自 动转 化 为 servlets。前 面 “hello”页 面 的 JSP版 本 看 起 来就是 下 面 这 样 :JSP testHello, .也 许 起初 JSP只是 作 为 简 单 引 用数据的 HTML文 件, 例 如 上 面的例 子 ,但 很 快 退 化 为 象 S
18、ervlets一 样 完全的将 代 码 和 HTML声明混 杂 在 一起, 实际 上 界 面 设 计 师 仍 然 无 法 修改 JSP文件。更重要 的 是 ,JSP带 来 不 好的面向 对 象 设 计 ,例 如 include文 件 就 是 类继 承 的 一种不 好 的 替 代 方 式 。JSP页 面不 能 子 类 化 ,使 得程序 员 很 难 提取通用 的 代 码 和HTML。Hunter 5总 结 了 JSP一些其它的 问 题 ,例如 不 太 优 雅的循 环 机 制 实 现 列 表 的 显 示 等 。尽 管 JSP不是最 终 方 案 ,但它 确 实 将 模 板 概 念 带 进 了程序 员
19、的 思 想 中 。模 板是一 个 HTML文 档 ,拥 有 一 些填充 点 ,可 以 使 用 数据或者是一 些 简 单 行 为 的 结 果来填 充 。不 幸 的 是 几 乎 每个模板引擎 都 在 重 演 JSP的 错 误 , 它 们 提 供 一 个 完 全 图 灵机 式 工 具 化 的 (Turing-complete tool-specific)编 程 语 言,嵌入 到 HTML中 ,就 像 JSP 一 样 ,然 后 设 计 师 不得不考 虑 用 程 序 处 理 各 种 突 发 情况,而 不 顾 及 页 面 模 型。尽 管 JSP模板引 擎 解 决 了 一 些 烦 人的 问题 ,很 多 人
20、仍 无 法 定 位 JSP不适 用 于大型 系 统 的 主 要 原 因 1。模 板 应 当只 是 呈 现 数 据 系 列的 视 图 ,完 全 与 后 台 数 据 处 理分开,只 是 显 示 后 台 数 据 处 理 的 结 果 。如 果 模 板 语 言功能 太 强 ,模 板 设 计 者 可能就会混 杂 模 板 和 业 务 逻 辑 ,下 面 的 章 节 详 细 说 明 为 什么 应 当 避 免 这 样 的 混 杂 。1尽 管 jG的 页 面 URLs以 .jsp结 束 ,其 实 根 本 就不 是 JSP文 件 ,去 掉 JSP后 缀 后 ,我 们 使 用 页 面 名字作 为 向 后 链 接 的 替
21、 代 。3. 分 离 动 机 使 用 模 板 引擎的 主 要 目 的 是 在 思 想 和 机制上 将 逻 辑 和 数 据 处 理与 显 示分 离 。Web站点 开 发 情 况 下 ,这 基本意 味 着 代 码 中 不 存 在 HTML,HTML中 没有代 码 。下面 是 程 序 员 和 界面 设计师 需要 这 种 分 离 的 一些原因:1. 封 装: 站 点 外 观 完 全 包 含 在 模 板中, 业务逻 辑 完 全 位 于 数 据模型 里 ,每 一 部 分 都 是 完 整的 实 体 。2. 清 晰 度 :模板不 是 生成 HTML页 面 的 程 序 ,它是界面 设计 师 或 程 序 员 可
22、以直接 阅读 的 HTML文件。3. 分 工 :编 码 人 员 开 发时 界 面 设 计 师 可 以 并 行 建 立模板 ,可 以 通 过 聘 请 一 个 界 面 设计 师 (通 常 不会太昂 贵 )降低程序 员 的 负 担, 还 可 以 节 约 沟通 成 本 :界 面 设 计师 可以不用 与 程 序 员 交 流 直接修改HTML。jG开 发 过 程 中我 们 不断的 验 证 这 种 界 面 设计师 和 编码 人 员 独 立 工 作 的方式。4. 组 件复 用 :正 如 程 序 员为 了 提 高 清 晰 度 和 复 用 能力 ,将大 的 方 法 切 分 成 小 的 方 法一 样 ,界面 设计师
23、可 以 轻 松 的将模 板 提 取 成 一 系 列 子 模 板 ,例 如 功能区 、导 航 栏 、搜 索 框 、数 据 列 表 等 。混 杂 的模板很 难 提 取 ,并且 很 难 与 其 它 的 数据源一起搭 配 复 用 。5. 单 点 变 化 :因 为 能 够 提取 模 板 ,界 面 设 计 师 就 能 够 抽象出 链 接 一 样 小 的 元 素 ,以 及 用 户记录视图 一样 较 大一些的 项 目 。假如以后需 要 改 变 站 点 中每个用 户 列 表 的 样 式 ,界面 设 计 师 只 需 简 单 的修改一个 模 板 文 件 ,这 也避免某个行 为 变 化 时 修 改 多个地方而引 入
24、错 误 。模型中 某 个 行 为 变 化 时 只需要修改 一 个 地 方也非 常 重要 ,需 要 在 模 板 中 避 免 逻 辑 ,例 如 “是管 理 员 ”这样 的 逻 辑 可 能 会在模板的多 个 地方重复。6. 维 护 性 :更 换 站 点 外 观 只 需 要 修 改 模 板 而 不 是 程 序 ,修改程 序 的 风 险 远 远 大于修改模板 。另 外 ,修 改 模 板 不 需要重 启 正 在 使 用 的 服 务 器 应 用, 而 代 码 修 改 通 常 需 要 重新 编 译 和 重 启 。7. 可 更 换 的 视 图 :将 数 据 模 型 和 显 示 混 杂 ,页 面工程 师 无法 象
25、 使 用 “皮 肤 ”一 样轻 易 的 更 换 新的 站 点外 观 。在 jG,每 一种 站 点外 观 是 一 个 叫 做 分 组 的 模 板集合 ,分 组 之 间 的外 观 可 能 完 全 不 一 样(针对 那些 简单 的 更 换 颜 色 、字 体 而言 )。显 示 页 面 时 ,一 个 简 单 的指示 器 告 诉 页 面 控制器 该 使用哪 个 模 板 分 组 。8. 安 全 性 :模 板 应 用 于 页 面 客 制 化 在博客 中 是 一 个 通 用的功 能 ,不 过 正 如 微 软 Word中 的 宏一 样 ,自 由模 板 带 来 严 重的 安 全 隐 患 。S的 博 客 在 clas
26、s loader调 用 方 面 经 受 大量的 攻 击 之 后 ,从Volocity 19切 换 到 了 StringTemplate。人 们 也 会 想到一 种 简单 但 有 效 的 攻 击 方 式 -死 循 环 ,正如 本 文所 支持的 观 点 ,严 格 的 将 模 型 和 视 图 隔离, 禁 止 视 图 中 的 控制指令,等 于 安 全 性 的 提 高 。程 序 员 常 常 认为 也 不 断 的 争 论 ,严 格 的操作 花 费 更 多 的 时 间 。尽管在小 规 模 情 况 下 也 许 是 这样 ,例 如 在视图 中 添 加 一 块 新 的内容。我的 经 验 是 在 周 期 长 的 项
27、 目中, 项 目 进 展 会 更 快 ,具有更灵活 、健 壮的 代 码 ,这都得 益 于 本 节 中 列 出的各种突出 的 优 势 。4. 模 型 视图 控 制 器 模 式众 所 周 知 的模型 -视 图 -控 制器 模 式 9在 web服 务 器 设计 方面 运 用 的 很 好 ,它 提 出 三 个 领 域 ,分 别 属 于 页 面 生成 过 程 中 不 同 的 处 理部分。大 致 上 讲 ,视 图 代表 页 面模板 或 范 本 ,控 制 器既代表服 务 器 将 URL映 射 到 负责 处 理 的 代 码 片 断 的 分 发 机制,也 代 表 负 责 处 理 的代 码 片 断 本身, 模 型
28、 代 表 应 用程序数据 (或 者 状 态 ),绝 大 部分 业 务 逻 辑 ,以 及 任何跟模型 相 关 的 处 理。不 幸 的是程 序 员 们 不 确 定哪儿 是 明确的 区 分 这 三 个 领 域的 界 线 ,从 2002年开 始 一个 标 题 为 “在 MVC中 将 C和 V分离 ”12的 邮 件列表 在 进 行着 这 些 讨 论 ,出 现 这 些 最主要 是 因 为 控 制 器 和 模 型 的 边 界 模 糊 不 清 。相 比 之下 ,尽 可 能 的 使 视图简单 ,使 它 从 与 模 型 和 控制器的混 杂 中 脱 离 出 来 ,是普遍 认 同 的 观 点 ,是 值 得 努力的方向
29、。 以 我 的 经验 来 看 ,控制 器 应 当 尽 可能的 轻 量 级 。控 制 器 的 作用象是一个事 件 管 理 器 ,指 示 页 面代 码 什 么时 候 应 当 执 行 。不 同 页 面的代 码 片 断 也 都 是 控 制器的 一 部 分 ,应 当 限 制 使 他 们仅 仅 是 从 模 型 、会 话 或 者参数 列表 中 提 取 正 确 的 数据 ,输 出 到 视 图 中 。如 果 页 面 是 HTTP POST或 者 其 它 请 求 页 面的 最 终 处 理 者 (译 注 :例 如 ASP.NET中有 的 页 面直 接 是 Response.Redirect到 其 它 页 面 ,最
30、终处 理 者 就是它 重 定 向 的 目 标页 面 了 ),这 个页 面 将 触 发 模 型 中 的行 为 。页 面 可 能 包 含 一 些 自己特 定 的 数 据 处 理 ,但 通 常最好是从 页 面 中 将 这 些 代 码 提取 到模 型 中 ,发 挥 模 型的作用或者 封 装 通 用 的 操 作 。页 面中 对 模 型 连 续 一 系 列的方 法 调 用 ,也 应 当 提 取 重构成 模型 中 单 一 的 方 法 ,例如, 页 面 可 以 完 成 简 单 的数据 过 滤 (可 能 使 用 模 型 提供的通用目 的 过 滤 机 制 ),但是 象 “处 理 论 坛 登 陆 请 求 ”这样 的
31、 操 作 应 当 被 编 码 在模 型 中 ,处 理 页 面 只 是 简单 的 调 用。模 型 包含所 有 的 业 务 逻 辑 ,运 算 ,以 及 状 态 (象 数据 库 之 类 的持久化状 态 )。我 见 过 一 些 学 生 项 目 和 商 业 程 序 员 的 应 用 ,将 SQL语 句 写 在控制器 中 ,更 惊 讶 的 是 写 在 视图 里 面 ,任 何 数 据 库 结 构的 变 化使 得 SQL扩 散到 的 所 有 页 面 和 视图 必 须 修 改 。无 论 如 何 SQL表很 少 能 成 为 合适的抽象 层 次 ,模 型 应 当将数据 库 中 原 始 数 据 处 理 成 对 象 以
32、及 对 象 间 的关系 ,也 要 封 装 象 “购买 图 书 ”、“注 册 和 发 送 邮 件 ”这样 经 常 执 行 的 操 作。视 图 应 当只 是 确 定 怎 样 显 示模 型 处 理完 的 数 据 ,怎 样 向 控 制器 传递 数据 ,这样 界 面 设 计 师 对 编 写操 作 视图 声 明 的 工 作 会 很 合适。 视图 不 应 当 是 程 序 的 一部 分 ,意味 着 视 图 不 能 修 改模型 也 不 能 处 理 数 据 。确 定是否 良好 的 隔 离 了 视 图 ,一个 比 较 好 的 方 法 是 视 图 是否能运用在 其 它 应 用 中 ,例如,如果模 板 显 示 一 个
33、表 中的元 素列表, 那 么 这 个 模板是 否 能 被 用 在 书 店 应 用 程序或者是 论 坛 站 点 上 ?模 型 和控制 器 之 间 的 分 界 线 有 时 比 较 模 糊 ,依 赖 于 具体的 应 用 ,但 它 们 与 视 图 之 间 的 界 线 是明 显 的 。5. 模 板定 义查 找 文 献 资 料没 有 模 板 的 定 义 ,Hunter 5指 出 了 这 是 模 板 引 擎 市 场 的 一 个 问题 。虽 然模 板 的 概 念 很 直 观 ,但形 式 的 定 义 可 以 提供一个通用 术 语 ,一 个 划 分模板引擎和 他 们 生 成 能 力 的方 法 。输 出 模板不 同
34、 于 生 成 输 出的程 序 ,模 板是一个 范 本 ,而 后 者是程序 处 理 产 生 输 出 。模 板是嵌入了行 为 的 输 出 文 档 ,显 示 模 板 时 由 引 擎 进 行 处 理 求 值 。JSP文件是 自 由 模 板 的 一 个 示 例 。定义 1. 自 由模板T 是 由 输 出字 符t i和 行 为表 达式 ei交 错 出现 的 列表:t0e0tieitnieni任何 ti可能 是空 字 符串,e i是 语法运算上无约 束的 。如 果 T中没有 ei,那么 T就 只 是一 个简单的 字 符 t0。定律 1. 自 由模板 等 同 于 Chomsky无 约 束 0型 语 法 (un
35、restricted Chomsky type 0 grammar),因此 可 以生 成 递归 可 枚 举 语 言 (recursively enumerable languages)。证明 . 因 为 ei在 处 理上无 约 束 ,实 际 上 可 能 是 一 个 图 灵机 (Turing machine),因 此 可 以 完 全 由 它 自己生 成 0型 语 言。这 个 结论 是 自 证 明的 ,不 是 很 有 意思 ,但 值 得 明 确 声 明 的 是自由模板是最 强 大 的 ,能 够 生成任何想 要 的 输 出 (例 如 图 灵 机 能 够处 理 的 )。在 一 些 L语 言 中 生成文
36、档可能需 要 多 个 嵌 套 模 板 ,因此 单 个 字 符 ti和 单 个 模 板 的 输 出 可能与 L或 者 它 的 子 句不 一 致 。通 常 字符被 特 殊 符 号 表 达式分 割 ,例如 下 面 是 生 成 HTML body的一个 简 单 HTML模 板 18:Hello, % user %.这个 例子中, t0 = ”Hello, ”,e0 = ”$user$”,t1 = ”. ”。表 达 式被嵌 入 在 字 符 中 ,词 汇 分 隔符使 用 %.%。为 了 与 L一 致 ,模板可能使用 环 境 语 言 的 词 汇 来 编码 一些 表 达 式 ,例 如 XMLC 2使 用 HT
37、ML的 SPAN标 签 :Hello Sample name.自 由 模板可 以 修 改 模 型 ,可 以 执 行任何 可 调 用 的 函 数 ,也 可以使用上下文 改 变 输 出 。例 如 ,模 板自 己 可以使 用 近 似 泰 勒 级 数的 sin(i)函 数 计 算 table行 的 颜 色 。有 意 思的是 使 用 这 种 模 板定 义 ,XSL 20样 式 表 根 本就不 是 模板 ,因 为 样 式 表 使用一 系 列 XSLT树 状 转 换 , 生成 的 是 XML或 HTML文档 。尽管声 明 更 自 然 化 而 不 是 机 器指令 式 的 ,XSL样 式表更 像 是 servl
38、ets一 样 的程 序 。最 后 值 得提 出 的 是 因 为 模板使 用 文本 规 格 定 义 (textual specification),这 并 不 限 制 模 板生成文本内容(generating text.)。例如在生 成 文 本 前 ,很 多 源 对 源 (source-to-source)的 转 换 器 可 能 会 执 行一系 列 树 状 转 换 。下面 是 一 个 模 板 使 用( 测试 版本 的 )ANTLR 14类 似 LISP树 型 符 号的 树 型 声 明 。#(ASSIGN ) ASSIGN是 拥 有 两个子表达式 和 的 根 字 符, 在 构建 树 之 前 先 处
39、 理 并填充 子 表达式 的 值 。6. 限 制性模 板( Restricted template)分 类 自 由 模 板 极 为强 大 ,但 模 板 的 能力和将模型 视 图 混 杂 的 能 力 之 间 有直接 关 系 。模 板 越 强 ,就越接近 图 灵机 语 言 ,另 外 ,模 板的能力与 对 界 面 设 计 师 这 样 非程 序 员 的 适 用 程 度 存 在 一个 对 立关 系 。 自 由 模 板 面 临 的 最 大 混 杂 问 题 是 模 板 会影响 模 型 。如 果 一 个 模板可 以 修 改 模 型 ,那也是 这 个 问 题 的 一 种 。因此 从 视 图 中 分 离 模型,首
40、先 应 当 限 制 模 板 只 能以只 读 方式 操 作 接 收 到 的 一系列数据 值 ,这 些 只 读 数 据 值 叫 做属 性 (译 注 :几 乎 文 中 所 有 提到的属性均指 这 种 只 读 数 据 值 ,这 种 只 读 只是相 对 于 模 板 /视 图 而 言 ,对 模 型 来 说 是 可 修 改 的 ),它 们 是 由 模 型 处 理 的 。属 性 可 能 是 像 4521这样 的 单 值 型 ,Jim, Frank, John这 样 的 多 值 型,或者是 像 (name=Tom, ext=5322)这 样 的 聚 合 型 。对 未 定 义 /未 设 置属 性 的 引 用 将
41、得 到 空 字 符串。 一 旦 限 制 模板只 能 操 作 一 系 列 的 属 性 (译 注 :注 意上 面 关 于属性的定 义 ,这 里 的 操作指 只 读 形 式 的 操 作)以免 产 生 边 际 效 应 (side-effect),那么 对 生 成 语 法 (generational grammar)类 似的 地 方都需 要 注 意 。属 性 是 生成语 法 的 终 结符 (terminals)(词汇 )(译注 :终结符是 句子 中 会 实 际出 现 的符号 ,对应 的 非 终 结符 在 句 子中不 实 际 出 现, 仅在推导中起变 量 作 用 ),模板是它的产生 式 (producti
42、ons)(规则 )。这 种 关 系产 生 了 一些 有 趣 又 有 用 的 结 果 ,即通 过 形 式 语 言 (formal language)为 工 作 带 来 补 充完善。 例 如 XML文 档 类 型 定 义(DTDs)其 实 是 上 下 文 无 关 语 法 8,因 此 ,具 有上下 文 无关 语 法 能 力 的 模 板可以 生 成任何 可 使 用 DTD定 义 的 XML文档 。值 得 欣 慰 的是一 个 非常 简 单 的 模 拟 下 推自 动 机 (push down automaton)(译 注: 下推自动 机 对 应 于上下 文 无 关 语法 )的 模 板 引擎 可 以 生 成
43、 XML文 档 ,像 第 8节 介 绍 的 ,在 设计时 就可 以 强 制 模 型 和 视图严 格分 离。本 节 定 义 模板的 限 制 性 分 类 ,依 据 是 Chomsky的 13型生 成 语 法 1:上 下 文 相 关 语 法 (context-sensitive)、 上下 文 无 关 语 法 (context-free)、正 则 语 法 (regular)。让 我 们 从最弱的 3型 正 则 语 法 16模 板 类 型 开 始 。定 义 2. 正 则模板 由0、1或 2个 符号 确 定 。第一个符号可以 是一 个 字 符 t或 者是 一 个属 性引用 a,第 二个 符号如 果 存 在
44、 的 话 必 须 是一个正 则 模 板 的 引 用 。正 则 模板可能 是 空 字 符 串 。换 句 话说 ,正 则模板就 是 t、a、t 、a ,或者 是 ,其 中 是正 则 模 板的引用。属性 和 模 板 引 用 是边际效应无关 的 。定 律 2. 正 则 模 板 生成正 则 语 言 (译 注 :语 言 学 定 律正 则 语 法 生 成 正 则 语 言 ,正 则 模 板 使用的 正 则 语 法 )。证明. 定 律 3上 下 文 无 关 模 板 的 相关 证 明 ;只 是 限 制了派 生 树 (derivation tree)(子 树 )只 能 在 右 边拥 有 规则 引用 和 相 关 模
45、板 引 用 。更 为 普 遍 的正 则 表 达 式 相 当 于 提 供 了 一个 简 单 的 方 式 来 认 识 正 则 模板。 模 板 可 能 重 复 一 系 列 的属性 和 字 符 :ai和 ti,实际 上 模 板 通 过 对 多 值 属 性循 环 来 显 示 列 表 。令 人 惊 讶 的 事 实 是 这 些 迷 你 型 的 模 板能 够 做 很多事 情, 例 如 下 面 的 模 板 ,用一个虚 构 的 模 板 引 擎 伪 正 则 表达式 符 号 生 成 使 用 分 隔的 用 户 列 表 :( $names “ )+这 儿 ()+操作 符 表示 “一 个 或 多 个 ”,它 应 该 知道
46、遍 历 names属 性的 多 个 值 。大 部 分 模 板 引 擎 实 际 上 应该是像 下 面 的 Velocity 19模 板 一 样 使 用 FOR循 环 类 型的 结 构 来 处 理 :#foreach( $n in $names )$n#end然而 这样为了 为 列表 中 的每个姓 名应用一 个 模板 (译注: 指 $n部 分 ,需 要提取 出来 作为一个子模板 ),需要 程 序 员 在 循 环 中引用另外一 个 模 板 ,将 这 个模板 应 用在 列 表 元 素 上 ,这 就是 说 模板 不 是 在 右 边 的 位置上被 调 用 。例 如 假 设 界面 设计师 想 提 取 一 个
47、 叫 做 item的 列 表 项 模 板 :$attr$其 中 $attr$是 对 属 性 的 引 用 ,item会 被 应 用 在 这 个 上 面 ($attr$类 似 面向 对 象 方 法 中 的 this指 针 )。这样 模板可 以 使 用 下 面 StringTemplate符 号 ,将 item子模 板 应 用 在 names的 每一 个 属 性 值 上 :$names:item()$虽 然 可以使 用 正 则 模 板 表达上 面 这 种特 定 的 输 出 语 言,因 为 从形式 语 言 16中 我 们 知 道 有 些 语 言本 来 就是非 正 则 的 ,因 此 通常存在正 则 模
48、板 无 法 生 成 的 输 出 语 言 。支 持 对 其 它 模 板全面 引 用 的 模 板 与 上 下 文 无关生成 语 法 相 关。定义 3. 上 下文无 关 模 板 是 行 为 被限制在只能 引 用 属 性 ai和 模 板 的 一 种 模 板 类 型。上下 文 无 关 模 板 可 以 是空 ,行 为 是 边 际 效 应 无关的( side-effect free)。定律 3. 上 下文无关 模 板 生 成上 下文无关语言 。证明 . (概 要 证 明 )我 们 可 以 提 供任何上下文 无 关 语 法 句 子 的派生 树 与嵌 套 模 板 树 结 构 之 间 的等价 性 。 上下 文 无
49、 关 语 法 的 派生 树 在叶 节 点 上 是 终 结 符 引用 ,在子 树 根 节 点 上 是 非 终结 符 ,子 树 对 应 于 一 个 应 用 规则 , 它的 下 层 是 终 结 符 或者其它非 终 结 符 子 树 。模 板由字 符 列 表 、属 性 引 用 和 模板引用构成 ,可 以 把 模 板 的属性 和字 符 看 作 终 结 符 ,把 模 板 引 用 看 作 非 终 结 符 ,这样 我 们 就 明 白 对 应 于 派 生 树总 会 存 在 一 个 等 价 的 模 板 ,反 之亦然。 如 果 上 下 文无关模板等 价 于 一 个 上 下 文无关 语 法 ,那 么 这 个 模 板 可以生成上下 文 无 关 语 言。为 了 清楚上 下 文 无 关 语 法的生 成 能力 ,回 想 一 下 ,大 多 数 编 程 语 言的分析 器 通 过 LL-和 基 于 LR-分 析 器 生 成器 可 接 受 的 语 法 实现 ,他 们 都 是 上 下 文 无 关 语 法 17的 子集。因此,上 下 文 无 关 模 板 足以生成符合 普 通 编 程 语