Vincent.Chan‘s Blog

常用链接

统计

积分与排名

网站

最新评论

Domain Pollution Resolution 域污染解除

主题 Domain Pollution Resolution 域污染解除
作者 buaawhl

Domain 名词解释

首先说明一下 Domain 在本文中的意思。

<<Domain Driven Design>> 一书,令 Domain 这个词很火。引起了广泛争论:哪些 Logic 应该放在 Business Service Layer, 哪些应该放在 Domain Object 里面。这类争论纷纷扬扬,最后通常都上升到哲学高度,世界观高度,认知心理学高度。如果不幸发展到极端情况,双方很可能开始相互质疑对方的智商和对世界的 基本理解能力。这个层次的Domain 纷争,不是本文所关心的话题。

我一向认为,设计能够满足如下的条件,就可以称为是一个好的设计:能够使用多态性,代替 Hard-Coded if else switch 等逻辑分支;新需求来的时候,不需要在旧代码里面添加 if else switch等逻辑分支,而只需要加入一个新的 Class;尽量避免 Package, Class 级别的交叉引用。

本文所关心的Domain 是一个更高的层次,类似于DSL, Domain Specific Language 里面的Domain的意思。

本文主要讨论如下议题:

  • HTML是否只应该用来表示结构?
  • HTML是否应该包含逻辑?
  • HTML是否应该包含服务器端脚本逻辑?比如, JSP, Velocity, Freemarker.
  • HTML是否应该包含浏览器端脚本逻辑?比如, Java Script.
  • Java 代码中是否应该输出 HTML标签?比如,Taglib, Tapestry Page Component, etc?
  • Java 代码中是否应该使用框架特殊的 HTML View Model? 比如, XMLC, Wicket, Echo, etc.
  • HTTP Web Server 本来的设计宗旨是无状态,支持大用户量,多连接。目前的server side的 page flow, web flow, continuation, 力图使得HTTP Web Server 保持用户的流程状态。这种做法是否应该推荐?
  • SQL是一种可读性很好,应用广泛的一种 DSL。如何动态拼装SQL ,一直是一个难以解决的问题。
  • 是否应该在Java 代码中嵌入SQL?
  • 是否应该在Java 代码中使用 Criteria API?
  • 是否应该在SQL Template 中加入动态语言脚本逻辑?比如 iBatis 使用XML if otherwise, OR Bridge 中使用velocity?
  • HQL, OQL 在SQL中引入了OO 语言的特性,似乎操作的是对象数据库,而不是关系数据库。这种做法是否应该推荐?
  • Hibernate 动态期间篡改用户代码的做法,是否应该推荐?
  • JDO静态期间篡改拥护代码的做法,是否应该推荐?
  • Dynamic Proxy, CGLib 等动态篡改用户代码的做法,是否应该推荐?
  • Reflection 绕开了 Java 的类型检查机制,是否应该推荐?
  • Thread Local 是一种隐式契约,相当于绕开了显式契约(方法签名method signature),这种做法是否应该推荐?
    这些属于开发模式、代码风格方面的问题,正如所谓 Code Smell的感觉一样,也避免不了主观的成分。所以,下面的陈述并非论断,而只是"在我看来"。

HTML Template Layer

HTML应该尽量只用来表示结构,排除所有逻辑。

JSP, Velocity, Freemarker 等服务器端脚本逻辑,属于Java Code或者 Script Code对HTML 的污染。

不仅应该排除JSP, Velocity, Freemarker 等服务器端脚本逻辑,而且应该排除浏览器端脚本逻辑,比如Java Script.

当然,这里不是说,不用 Java Script,而是说,不要把Java Script 放在HTML里面。把Java Script 放在一个单独的.js 文件里面,在HTML中引入,并使用 CSS把HTML Element 和 Java Script Event Handler 联系起来。

这方面的资料有:

Unobtrusive Javascript
http://www.onlinetools.org/articles/unobtrusivejavascript/
http://www.kryogenix.org/code/browser/aqlists/
http://www.bobbyvandersluis.com/articles/goodpractices.php

如何排除HTML 中的Server Side Logic 呢?

Ajax!

Ajax fans 一定反应迅速,给出答案。

没错。Ajax 可以做到。Ajax 的主要问题在于使用和掌握难度。

除了Ajax 呢?

有几个选择,XMLC, Jivan, fastm, Wicket, Echo 等。(and Tapestry?)

fastm 是我做的一个模板层,具体内容可以在此下载。
https://fastm.dev.java.net/files/documents/1911/25042/fastm1.0c.zip

fastm采用 XML Comment 作为文档结构标记。注意,是结构标记,而不是逻辑标记。不含有 for if else。

可见,fastm 并不是毫无污染,只是没有逻辑污染。fastm 只在HTML中添加了自定义的结构标签,也勉强算没有违背 HTML只用来表示结构的原意。

Ajax, XMLC, Jivan 的HTML很干净,而且对HTML 结构的控制能力很强,能够用代码动态控制布局。fastm 也能够动态控制布局。Site mesh, tiles taglib, jsp, velocity, freemarker 等做不到。

fastm 里面的例子中,包括一个分页的例子,和一个动态组装 SQL的例子

Java Web Layer

Taglib, Tapestry Page Component, Echo, Wicket 等在Java 代码中输出HTML 标签,这也是一种,而且要求在Java 代码中使用框架特殊的HTML View Model,比如 Table, List, Label, Formbean 等View Object 。使用了这些框架,Java 代码的编译就需要依赖于这些框架了。

这相当于HTML 标签对Java 代码的污染。

JSP, Velocity, Freemarker 都不存在这种情况。Java 代码只是提供 POJO,然后 HTML 里面的Server Side Script使用这些 POJO.

fastm不支持逻辑,所以这个方面弱一些。 fastm要求Java Code 把if, else 等逻辑分支变成一个Map 结构。由于 Map属于java 的基本类,所以,fastm也不需要在 Java code中使用任何特殊的框架相关的view model 。

Web Layer - Stateless or Stateful

与其在服务器端支持 page flow, web flow, continuation,那不如在浏览器端支持状态。这种场合下,我觉得,正是使用 Ajax的最佳场所。

我倾向于这样的设计,服务器端尽量无状态,如果确实需要状态,那么尽量在浏览器段保持状态。

lightweb 是我做的一个Web框架。
https://lightweb.dev.java.net/files/documents/4371/25044/lightweb0.9b.zip

主要的特点是,URL-Centric, 鼓励无状态的Service Oriented 设计。支持三种Service模型,

  • Action (like WebWork Action),
  • Controller (like Spring MVC Controller),
  • Channel (like Struts Action Dispatcher),

lightweb 支持多级模块寻找,使用几条匹配规则,代替庞大的Site Map 配置文件。

lightweb 查找View的时候,不是根据Site Map 文件里面的那种小型状态机的配置(success > showIt.jsp; fail> this page), 而是直接资源定位。lightweb继承了 fastm的观点,把template 看作资源,而不是一段运行脚本。

Lightweb 设计为IoC友好。在各个环节都暴露出插件接口。程序员可以自己选择 IoC策略。可以采用一般的做法,用一个IoC container 集中管理,比如,Spring IoC, Pico, Nano等;也可以分散到各子模块,进行管理;也可以分散到每个 Action Validator里面管理。

ORM Layer

Hibernate 动态期间篡改用户代码,JDO静态期间篡改用户代码。

令我想起黑客,木马,夹带,偷梁换柱等。这属于语义上的 Pollution。就是说,从原有的Source 看起来,Domain Object并没有那样的行为,运行起来却有那样的行为。

iBatis采用 reflection,确实干净。但是,性能、功能上又不够强大。某些功能的实现,确实需要代码生成。

lightor是我做的一个 ORM框架。

https://lightor.dev.java.net/files/documents/4370/25043/lightor0.5a.zip

lightor也需要代码生成。区别在于, lightor不修改任何代码,只是产生新的 Mapper代码。Domain Object 运行的时候,还是你原来的Domain Object。这就避免了语义的污染。

lightor生成的 Mapper代码,可以编译期类型检查,可以阅读,跟踪,调试。这些生成的代码,也都遵循本文的原则。 Java 代码里面不含有SQL 片断,SQL 都存在于单独的资源文件中。

(另外,不采用CGLib ,也是为了实现的简单)

lightor直接使用 Native SQL。Lightor 的目的不是屏蔽关系数据库,而是恰好相反。lightor 的目的是,帮助程序员更清楚地认识关系数据库,SQL, JDBC。lightor 和JDBC不冲突,可以一起使用。

lightor努力的第一个主要方向就是效率。大数据量查询和处理的效率。

据我所知,目前只有 lightor才支持大数据量批量处理,因为只有lightor 不惜降低ORM的身份和封装层次,能够接受 ResultSet作为参数。

lightor的缓存策略比 Hibernate更前进了一步,把 query cache 和 ID Cache 集成在一起。以便得到更好的控制。并且,把缓存API暴露给程序员,以便智能的控制某个特定的缓存。

SQL

HQL, OQL 在SQL中引入了OO 语言的特性,似乎操作的是对象数据库,而不是关系数据库。

我感觉,这是一种语法层次上的 Domain Pollution。OO 语法对SQL语法的污染。

正如感觉LinkQ 是数据库查询语法对OO 语法的污染。

Java 代码中使用 Criteria API,也属于数据库查询领域对 OO领域的污染。

SQL是一种可读性很好,应用广泛的一种 DSL。

我的看法是,尽量把完整的 SQL放到单独的资源文件中,可以直接Copy 到SQL Client就可以运行。特殊优化过的 Native SQL,也是同样的地位。

HQL, OQL 阻碍了用户对SQL进行特殊优化。

如何动态拼装SQL ,一直是一个难以解决的问题。

直接在Java 代码中嵌入SQL ,肯定不行。前面说了,Java里面最好不要有 SQL片断,最好放到另外的资源文件中。

在Java 代码中使用 Criteria API? 前面也否定了。

在SQL Template 中加入动态语言脚本逻辑?比如 iBatis 使用XML if otherwise, OR Bridge 中使用velocity?

这种方法还是不错。不过仍然在 SQL里面引入了脚本逻辑。

我也没有好的做法。我采用 fastm来处理。在SQL 里面引入结构标记。这也相当于污染。因为SQL和 HTML不同。SQL 不是描述结构的,而是一门DSL。

这种做法的一个好处是,整段 SQL还是可以Copy 到SQL Client里面,稍微修改一番,就可以直接运行。

A Demo Forum

farum是我做的一个简单的 forum demo. 使用了 fastm, lightor, lightweb, 也尽量体现我前面提出的原则。
https://farum.dev.java.net/files/documents/4372/25045/farum0.5a.zip

我觉得,使用一门语言,应该尽量发挥它的特长,而不是针对它的短处,修修补补。

Java作为静态类型编译语言的好处,就是编译期类型检查。那么如何发挥这个优势?前面说的 Mapper源代码生成,就是一种尝试。

另外,其它的类似的胶水粘合部分,比如, IoC, Validation等,都可以进行这方面的尝试。目前,farum 就是采用这样的做法,自己写的Validator Source进行 validation, value setting, service implementation injection等工作。

farum没有使用 Dynamic Proxy, CGLib等动态篡改用户代码的做法。

尽量不使用Reflection ,因为Reflection 绕开了 Java 的类型检查机制。与其使用 reflection,真不如使用动态脚本语言。

当然,reflection 是非常难以避免的。farum 也少许使用了reflection。

Thread Local 是一种隐式契约,相当于绕开了显式契约(方法签名method signature)。 WebWork的一些用法,Spring Framework 的Open Session In View,等都是采用了 Thread Local。

farum没有采用 Thread Local,而是采用了另一种方法实现了 Open Session In view。而且如果缓存命中,甚至不需要从connection pool 中获取Connection 。

总结

<<Web 开发构想>>一文中,我提出了,理想中的 Web开发架构是这样的:

开发速度快,运行速度快,结构清晰优雅。

具体到每一层:

  • Web 框架层主要追求 开发速度快。
  • O/R 层主要追求 运行速度快。
  • 页面资源层和页面模板层主要追求 结构清晰优雅。

下面就是我对这些理想的初步实现。

还远远达不到完美的程度,而只是朝这个方向努力。

Template Layer

fastm
https://fastm.dev.java.net/files/documents/1911/25042/fastm1.0c.zip

Web Layer

lightweb
https://lightweb.dev.java.net/files/documents/4371/25044/lightweb0.9b.zip

ORM Layer

lightor
https://lightor.dev.java.net/files/documents/4370/25043/lightor0.5a.zip

A forum demo using fastm + lightor + lightweb

farum
https://farum.dev.java.net/files/documents/4372/25045/farum0.5a.zip

----------

由于项目可能还没有通过java.net的申请流程,所以无法公开下载。
我把所有的项目都放在fastm的这个目录下。
https://fastm.dev.java.net/servlets/ProjectDocumentList?folderID=4520

posted on 2006-02-23 21:56 Vincent.Chen 阅读(2531) 评论(0)  编辑  收藏 所属分类: 杂文


只有注册用户登录后才能发表评论。


网站导航: