级别: 初级 IBM, 作者
2003 年 3 月 01 日 魔法 Servlet 反模式
返回原文
最常见的服务器端 Java 反模式可能就是魔法 Servlet(Magic Servlet)了。当程序员试图在一个单独的 servlet 中进行太多的操作时,就会出现这种陷阱。可以预见,它会不受制止地增长,直到最后连微不足道的维护都会象恶梦般可怕。我在大学中第一次认真地做编程工作的时候,还没有什么组织技巧。我的程序就象一堆可怕的、无组织的垃圾。随着时间推移,我开发出一些工具,学会了一点技术(如面向对象的编程),这些都极大地增强了我的组织和划分能力。当我转向服务器端 Java 编程时,我没有再犯有关 servlet 的相同错误,不过我发现我的很多客户都会犯这种错误。当我发现有很多优秀的程序员在构建 servlet 时很少组织或根本不组织时,我很惊奇。最初,这种行为令我很迷惑,但经过思考其中的原因之后,我明白了为什么会这样。
您很可能已经了解到分离表示和业务逻辑是个好主意。有些人将这种技术称为模型-视图分离。您很可能也知道这种分离的根源要追溯到最早的设计模式之一:模型-视图-控制器。客户机/服务器体系结构有一个逻辑分离点:客户机和服务器之间的那条线。通常,您将模型放在服务器上,而将视图放在客户机上。随着时间推移,很多框架代替了控制器的角色,而模型-视图分离也变得微不足道。图 1 展示了这种趋势。
图 1. 客户机/服务器应用程序中的模型-视图分离。
问题:Servlet 可能隐藏视图逻辑
当我们试图将这种方法应用到 servlet 模型时,假定浏览器的 HTML 为视图是很正常的,它用 HTTP 命令调用 servlet,触发模型逻辑。这种假定就意味着您根本不需要担心模型-视图分离,对吗?错。
让我们来考虑一个显示留言板消息列表的 servlet(请参阅
清单 1)。您可以使用 HTTP GET 调用 servlet。组织过程很简单。进行调用的 HTML 代码就是视图,而 servlet 就是模型。让我们看看一部分程序:
清单 1. 隐藏视图的模型
ResultSet rs =
stmt.executeQuery("SELECT subject, author, board" +
" from posts");
// display the result set
// rs.next() returns false when there are no more rows
out.println("<h1>Message board posts</h1>");
out.println("<TABLE border=\"1\">");
out.println("<TD><b>subject</b></TD>\n");
out.println("<TD><b>author</b></TD>\n");
out.println("<TD><b>board</b></TD>\n");
|
如果您还没有找到陷阱,请注意其中的查询看起来有点象从数据库读取数据的模型逻辑,而
out.println 语句中的信息看起来象是 HTML。我们正在混合模型和视图逻辑。这是怎么发生的?这种设计和传统的客户机/服务器设计之间的区别在于我们的解决方案是面向批处理的,它既需要上行视图(调用 servlet)也需要下行视图(显示结果)。客户机/服务器程序员习惯于使用单独的视图:我们使用纯 HTML 创建上行视图。但是在本示例中,我们使用 servlet 中的 print 语句创建下行视图。图 2 展示了隐藏下行视图的问题。
图 2. 模型中的隐藏视图逻辑
现在,问题就完全清晰了。servlet 的作用是充当下行视图和模型。早期的交互式客户机/服务器程序并没有这种问题。但在我们的示例中,因为 servlet 实际上创建了下行视图,所以我们需要另外的组件来包装控制器和模型逻辑。
重构的解决方案:应用模型-视图-控制器
这个问题的解决方案是使用一种修改过的、更适合基于 servlet 编程的模型-视图-控制器来进行重构。我们需要同时考虑上行和下行视图。有些人会说显式控制器没什么必要。我更喜欢使用控制器,因为我可以使用它来触发模型逻辑,然后进行某种验证处理,接着将用户路由到一个相应的错误处理或结果页。图 3 展示了我们重构之后的体系结构。
图 3. 重构的解决方案
下面的程序将帮助我们更详细地观察每一块。(请牢记这些程序是源自于
Bitter Java,它们仍需要更进一步的重构。例如,我们不使用连接池,也不使用通用命令接口。)
- servlet 的作用是充当控制器(请参阅
清单 2)。它就象交通警察一样。上行视图通过 HTTP 调用 servlet 控制器。servlet 的职责是执行任何业务逻辑,然后根据输出将结果传送到合适的下行视图,也就是 JSP 页。
- Java 命令就是我们的模型逻辑(请参阅
清单 3)。请将命令想象成业务逻辑周围很薄的一层包装。简单的命令结构就可以使其很容易被创建到工具中,如 WebSphere。使用类似于 XML 转换之类的方法将命令生成自动化也很容易。命令 API 很简单:您使用方法来创建它们,再设定任意的输入参数,接着执行它们,然后获取结果,最后销毁它们。
- JSP 页是我们的下行视图(请参阅
清单 4)。JSP 页是一个看起来很象 HTML 的模板,它含有直接插入的 Java 代码和托管动态内容的 bean 占位符。servlet 将命令的结果以 bean 的形式传送到 JSP 页。然后 JSP 容器将 JSP 编译成 servlet 并执行它,从而创建 HTML。然后应用程序服务器再将 HTML 送回客户机。
我们极大地受益于这种方法,其中有很多原因:
- 命令层很容易自动化。实际上,WebSphere 向导和 VisualAge for Java 都使用类似的方法。如果您正在进行命令行开发,您就可以轻松地创建命令存根。
- 模型-视图-控制器将用户界面与模型改变隔离(反之亦然),这使得维护更加简单。
- 这种方法避免了特殊性,因此设计师和程序员就可以使用熟悉的工具和语言了。
数十年来,程序员们使用模型-视图-控制器简化了代码结构,并使得维护任务更加容易。如果您已经在用这种方式构建代码,那么您就可以更好地识别和重构不好的实现。您不必自己去创造,象我这样做就可以了。您可以使用 WebSphere 的向导创建大致符合该编程模型的代码。如果您更喜欢使用开放模型,就请查看 Jakarta 的 Struts 框架。您还可以使用会话 EJB 组件来代替命令层。不管您如何选择,请确保您的模型和视图之间实现了完全的分离,从而避免最基本的服务器端反模式:魔法 Servlet。
如果您还想阅读更多关于这种编程模型的内容,请参阅
参考资料,那里有一个相对比较旧,但很实用的 IBM 红皮书,名为
Design and Implement Servlets, JSPs, and EJBs for WebSphere,它很好地详细说明了整个体系结构。您还可以看看我的
Bitter Java,这本书包含两章完全关于魔法 Servlet 和相关反模式的内容。
返回原文
关于作者  | |  | IBM has authored this article |
对本文的评价
|