Skip to content

四火的唠叨

一个纯正程序员的啰嗦

Menu
  • 所有文章
  • About Me
  • 关于四火
  • 旅行映像
  • 独立游戏
  • 资源链接
Menu

页面模板的重构

Posted on 11/18/201206/23/2019 by 四火

page_divide 最近在工作中重构一个老系统,烂的地方有很多,但是对于后台的页面模板(我指的是 JSP、FreeMarker、Velocity 这样的后台模板,JavaScript 前端模板不在此讨论范围内),却是我要说的部分,这似乎是一个被人遗忘的小角落。你可以很轻易地找到怎样重构 Java 类和方法的材料,你的 Java 代码可以写得很优雅;去搜搜 “重构”,到处是怎样重构你的 Java 代码、C++代码,我们也能找到许多前端设计师对于页面结构的重构,但是重构的范围远非至此。

后台业务逻辑写得再好,一个 jsp、ftl 模板页面还是可能写上几千行,业务逻辑耦合在呈现代码里面,看起来一团糟,对这部分的重构,既不属于传统的 Java 代码的重构,也不属于前端工程师的范畴,就这样姥姥不疼、舅舅不爱地被忽略了。但是对一个网站来说,模板数量大得惊人,这是一个不可避开的话题。微博上 @zhh-2009 说道:

阿里集团有成千上万的系统,一大堆的 Java 码农,有个更好笑的说法是: 阿里 Java 码农其实就是 velocity 模板码农。

把业务逻辑从模板中剥离出去

模板是用来做什么的?就是用来做页面生成和展现的,以分离业务逻辑代码和用户界面代码。理想情况下,模板代码中不应该包含任何业务逻辑的代码在里面。我见过通过向模板传递 service 对象的方式,再在模板里面通过 service 获取数据,这是糟糕的做法。模板要做的事情需要保持清晰,不要耦合那些模型层的业务逻辑。

剥离数据处理的重复劳动

Model 的数据,距离展现需要的数据,表现形式上会有诸多差异,所以往往在模板拿到以后,还需要经过加工处理才能展示。比如日期型数据,一个 java.util.Date 对象被送到了模板上,有时需要转换成 “2012-11-18” 这样的形式,有时需要转换成 “09:47:10” 这样的形式,于是我见到了大块的页面模板上数据处理的逻辑代码。

当然,这也是可以优化的:

1、使用标签。标签可以封装好一些通用的展示逻辑,这里指的标签就是纯粹为了展示的需要而封装的标签,并非封装了业务逻辑的功能标签。每一个标签都可以变成一个小的 MVC 组件,一样可以使用模板的方式来输出(而不是再标签实现类里面生写 HTML 代码)。

2、使用模板宏。比如 FreeMarker 的模板宏:

<#macro repeat count> 
    <#local name = "default"> 
    <#list 1..count as iter> 
        ${name} ${count}/${iter}: <#nested> 
    </#list> 
</#macro> 

然后就可以使用了:

<@repeat count=3>abc</@repeat>

3、使用数据处理的工具方法。可以写辅助类,在模板中引入,也可以利用模板的 “静态引入” 能力,后文会提到。

4、在数据传递到模板前,增加一层数据预处理的逻辑。这个实现方式有很多,比较常见的是利用 Interceptor,将一种数据处理成多种展现形式,在模板中就可以直接拿来使用,后文也会提到。

管理好模板数据的上下文

说到模板数据的上下文,就要提到模板变成页面的方式,最基本的方式其实就是占位符(表达式)的替换,也就是将指定模板内容(字符串)中的特定标记(子字符串)替换一下便生成了最终需要的业务数据。在此基础上,才有编译型或者解释型的其它方式。

在占位符替换的过程中,需要根据表达式字符串,去特定的上下文中寻找相应的数据,以 JSTL 为例,所有通过 request.setAttribute(“key”, value) 方法放置的数据全部都能直接获取:

<div><c:out value="key" /></div>

或者也可以利用 JSP 对 EL 表达式原生的支持:

<div>${key}</div>

如果你使用的是 OGNL 表达式语言,为了更好用,它在 Struts 2 中做了进一步扩展,这时这个上下文就是总接触到的 “value stack”。

展示数据预处理层

这一层就是为了填补 Model 和模板展示需要的数据或者数据形式之沟壑而增加的,可以使用 Interceptor 实现,比如我在所有页面展示里面都需要用到当前用户 user 这个对象,那我就可以在 Interceptor 中,把 user 对象准备好,放置到模板数据的上下文中,这样在模板里面就可以拿来即用了。这是数据的沟壑,还有数据形式的沟壑,前面那个时间格式的例子已经提到过了。

静态引入也是一种常用的准备数据的方式,在 JSP 里就是:

<%@import page="userData.jsp" %>

用这种方式引入的页面在编译时就会引入进来,里面的数据、包、类拿来即用。

子页面划分

在页面模板重构上,这大概是我们最常用和最基础的办法。我们经常根据最终呈现页面的特点,把页面划分成展示功能独立的几个子页面,然后在需要的位置引入进来,比如 JSP 的动态引入:

<jsp:import page="detail.jsp" />

还有一种方式对页面模板开发的程序员更加透明,开发人员在自己关心的页面模板中可以看不到这些 import 的代码,转而把这个引入的规则配置放到页面模板之外去,有的根据 URL 规则来聚合子页面,有的根据自定义的页面特点来聚合那些子页面,比如 Tiles 2 的聚合规则配置:

<definition name="index" template="/siteLayout.jsp">  
    <put-attribute name="header" value="/header.jsp"></put-attribute>  
    <put-attribute name="content" value="/content.jsp"/>   
    <put-attribute name="footer" value="/footer.jsp"/>  
</definition>  

更多的例子,在这个我曾经已经谈到过的这个页面聚合的话题。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

×Scan to share with WeChat

你可能也喜欢看:

  1. 模板引擎随谈
  2. 过多 if-else 分支的优化
  3. JavaScript 重构攻略
  4. MVC 框架的映射和解耦
  5. Java8 集合中的 Lambda 表达式

4 thoughts on “页面模板的重构”

  1. 甄码农 says:
    11/20/2012 at 5:26 PM

    tiles? 为了配置一个母板页再去单写一份 xml 配置,不知道做这个东西的人是咋想的? 我是极度不适应。 python,.net 都有现成的母板页,java 社区的老大为啥不学学呢?

    Reply
    1. 四火 says:
      11/20/2012 at 7:41 PM

      java 社区还真有你说的这种现成的母版页,不需要写复杂配置的,你可以看看我这篇文章,有提到:http://www.raychase.net/850

      另外,tiles 现在也支持正则表达式了,配置可以比你说的简单。

      Reply
      1. 甄码农 says:
        11/22/2012 at 5:04 PM

        你说的这个我没用过,用过一点 jsp 和 velocity。学习一下

        Reply
  2. 小杰_同学 says:
    11/18/2012 at 12:07 PM

    使用 jsp 自定义标签可以缓解这个问题,我喜欢在系统中开发大量的自定义标签

    Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

订阅·联系

四火,啰嗦的程序员一枚,现居西雅图

Amazon Google Groovy Hadoop Haskell Java JavaScript LeetCode Oracle Spark 互联网 亚马逊 前端 华为 历史 同步 团队 图解笔记 基础设施 工作 工作流 工具 工程师 应用系统 异步 微博 思考 技术 数据库 曼联 测试 生活 眼界 程序员 管理 系统设计 缓存 编程范型 美股 英语 西雅图 设计 问题 面向对象 面试

分类

  • Algorithm and Data Structure (30)
  • Concurrency and Asynchronization (6)
  • System Architecture and Design (43)
  • Distributed System (18)
  • Tools Frameworks and Libs (13)
  • Storage and Data Access (8)
  • Front-end Development (33)
  • Programming Languages and Paradigms (55)
  • Testing and Quality Assurance (4)
  • Network and Communication (6)
  • Authentication and Authorization (6)
  • Automation and Operation Excellence (13)
  • Machine Learning and Artificial Intelligence (6)
  • Product Design (7)
  • Hiring and Interviews (14)
  • Project and Team Management (14)
  • Engineering Culture (17)
  • Critical Thinking (25)
  • Career Growth (57)
  • Life Experience and Thoughts (45)

推荐文章

  • 聊一聊分布式系统中的时间
  • 谈谈分布式锁
  • 常见分布式系统设计图解(汇总)
  • 系统设计中的快速估算技巧
  • 从链表存在环的问题说起
  • 技术面试中,什么样的问题才是好问题?
  • 从物理时钟到逻辑时钟
  • 近期面试观摩的一些思考
  • RSA 背后的算法
  • 谈谈 Ops(汇总 + 最终篇):工具和实践
  • 不要让业务牵着鼻子走
  • 倔强的程序员
  • 谈谈微信的信息流
  • 评审的艺术——谈谈现实中的代码评审
  • Blog 安全问题小记
  • 求第 K 个数的问题
  • 一些前端框架的比较(下)——Ember.js 和 React
  • 一些前端框架的比较(上)——GWT、AngularJS 和 Backbone.js
  • 工作流系统的设计
  • Spark 的性能调优
  • “残酷” 的事实
  • 七年工作,几个故事
  • 从 Java 和 JavaScript 来学习 Haskell 和 Groovy(汇总)
  • 一道随机数题目的求解
  • 层次
  • Dynamo 的实现技术和去中心化
  • 也谈谈全栈工程师
  • 多重继承的演变
  • 编程范型:工具的选择
  • GWT 初体验
  • java.util.concurrent 并发包诸类概览
  • 从 DCL 的对象安全发布谈起
  • 不同团队的困惑
  • 不适合 Hadoop 解决的问题
  • 留心那些潜在的系统设计问题
  • 再谈大楼扔鸡蛋的问题
  • 几种华丽无比的开发方式
  • 我眼中的工程师文化
  • 观点的碰撞
  • 谈谈盗版软件问题
  • 对几个软件开发传统观点的质疑和反驳
  • MVC 框架的映射和解耦
  • 编程的未来
  • DAO 的演进
  • 致那些自嘲码农的苦逼程序员
  • Java 多线程发展简史
  • 珍爱生命,远离微博
  • 网站性能优化的三重境界
  • OSCache 框架源码解析
  • “ 你不适合做程序员”
  • 画圆画方的故事

近期评论

  • Ticket: TRANSACTION 1.922915 BTC. Go to withdrawal >> https://yandex.com/poll/enter/BXidu5Ewa8hnAFoFznqSi9?hs=20bd550f65c6e03103876b28cabc4da6& on 倔强的程序员
  • panshenlian.com on 初涉 ML Workflow 系统:Kubeflow Pipelines、Flyte 和 Metaflow
  • panzhixiang on 关于近期求职的近况和思考
  • Anonymous on 闲聊投资:亲自体验和护城河
  • 四火 on 关于近期求职的近况和思考
  • YC on 关于近期求职的近况和思考
  • mafulong on 常见分布式基础设施系统设计图解(四):分布式工作流系统
  • 四火 on 常见分布式基础设施系统设计图解(八):分布式键值存储系统
  • Anonymous on 我裸辞了
  • https://umlcn.com on 资源链接
© 2025 四火的唠叨 | Powered by Minimalist Blog WordPress Theme