设计模式之用设计模式开发J2EE系统 (1)

2008-05-24 05:34:57.0     浏览:211     来源:赛迪网论坛
关键词:

最近,一个大学时的朋友的A公司接了个电子商务的网站项目,他们的公司是那种作坊式的公司,是学校的老师创办的,我在大学期间也曾经在A公司工作了一段时间。我在学校时主要用C++Builder开发C/S系统的,而我在学校里,在编程上也算有一点名气,所以去到A公司后就使C++Builder成为公司的主要开发工具。好了,有点扯远了。而我的朋友又没有用过J2EE,所以我就成了他的启蒙老师。正是在这段时间,我也不段的回过头来看自己以前写的程序,发现我过去原来一个地方打转,而我不想我的朋友在学习J2EE时再走我的老路,所以我在一开始就教他何为设计模式,如果用MVC来设计自己的系统。
其实,我也是在工作之后才知道什么是设计模式。也正是在工作之后,才知道什么是OO,才发现原来自己以前一直用OO的语言(c++、Java)写的却是面向过程的程序。我们公司的项目进入尾声阶段了,这也让我有了点自己的时间,于是我决定花一些空闲时间写下这篇文章,希望给大家一些帮助,互相学习。
在这篇文章中,我不会讲述如何用Servlet或JSP一步一步地开发一个J2EE系统,因为这些资料书上或网上有一大堆,只会简要介绍一下有关知识或把一些比较重要的知识点写出来。我要讲述的是如何用Servlet、JSP和Bean,并结合一些常用设计模式开发一个可扩展、重用性好的系统。在这里,我也并不会深入地、全面讲解每一种设计模式,但我会在讲解每一种设计模式的时候都以实例来对它进行讲解。同时,每学习完一个模式,我们对会应用它来完善或扩展之前的实例,所以,在整篇文章最后,开始的那个简单的实例会变成一个简单的B/S系统。好了,废话就讲到这里了。
这个教程的目录如下:
一 简单介绍j2ee
1 servlet
2 jsp
3 javabean
二 传统的设计方法及其弊端
1 一个简单的实例
2 这种设计方法的弊端
三 用实例学习模式
1 设计模式的介绍
1.1 什么为设计模式
1.2 为什么要用设计模式
2 用实例学习常用的设计模式
2.1 MVC模式
2.2 控制器模式
2.3 分发者模式
2.4 工厂模式
2.5 命令模式
2.6 过滤器模式
2.7 DAO模式
2.8 值对象模式


四 一个完整的系统框架

一 简单介绍j2ee
由于EJB的复杂性,所以在这个教程中的所有实例都不会用EJB进行讲解,所以这里不讨论EJB。在这一节,只是简单的介绍一下j2ee中最常用到的三个组件:Servlet、JSP和JavaBean,因为任何一个J2EE系统都离不开这三个组件。
1 servlet
1.1 关于doGet()和doPost()方法
Serlvet接口只定义了一个服务方法就是service,而HttpServlet类实现了该方法并且要求调用下列的方法之一:
doGet:处理GET请求
doPost:处理POST请求
doPut:处理PUT请求
doDelete:处理DELETE请求
doHead:处理HEAD请求
doOptions:处理OPTIONS请求
doTrace:处理TRACE请求
那么我们在什么时候应该调用什么方法呢?通常情况下,在开发基于HTTP的servlet时,开发者只需要关心doGet和doPost方法,其它的方法需要开发者非常的熟悉HTTP编程,因此这些方法被认为是高级方法。只要在web.xml中配置好了相应的映射,那么,当client(browser)产生一个请求时,Servlet会根据请求的类型自动调用相应的doXXX方法:如果请求是get请求,则会调用doGet方法;如果请求是post请求,则会调用doPost方法。

1.2 关于Servlet线程的问题
除了在Servlet实现SingleThreadModel接口,在缺省情况下,一个容器只为每个Servlet类产生一个对象(实例)。当多个请求要调用同一个Servlet时,容器会为每个请求创建一个线程。所以,我们不能在Servlet中定义实例变量,只能定义常量。如:
public class SomeHttpServlet extends HttpServlet {
HttpSession session;
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, java.io.IOException {
session = request.getSession();;
}
}
上面的代码是错误的,因为所有线程都共用一个session变量。
2 jsp
JSP其实也是一个Servlet。当你要调用一个JSP时,实际上JSP会先被翻译成Servlet,然后执行由JSP翻译而成的Servlet。当要用一个JSP输出信息时,这个Servlet会把JSP中的HTML代码和JAVA代码生成的内容以HTML文本的形式传回给客户端。当然,JSP翻译成Servlet的工作是由容器完成的,并且只有在一个JSP在第一次被调用时需要进行翻译,以后调用时就可直接调用该JSP所生成的class文件。由此可见,JSP是完全可以由Servlet来取代的。但是,在Servlet中,我们需要把要生成的页面的HTML代码放入到Servlet中,而在JSP中我们只需要把JAVA代码嵌入HTML代码中。下面是JSP翻译成Servlet后的代码的框架:


  1. package org.apache.jsp;
  2. import javax.servlet.*;
  3. import javax.servlet.http.*;
  4. import javax.servlet.jsp.*;
  5. import org.apache.jasper.runtime.*;
  6. public class HelloWorld_jsp extends HttpJspBase {
  7. ......
  8. public void _jspService(HttpServletRequest request,
  9. HttpServletResponse response)throws java.io.IOException, ServletException
  10. {
  11. JspFactory _jspxFactory = null;
  12. javax.servlet.jsp.PageContext pageContext = null;
  13. HttpSession session = null;
  14. ServletContext application = null;
  15. ServletConfig config = null;
  16. JspWriter out = null;
  17. Object page = this;
  18. JspWriter _jspx_out = null;
  19. try {
  20. _jspxFactory = JspFactory.getDefaultFactory();
  21. response.setContentType("text/html;charset=ISO-8859-1");
  22. pageContext = _jspxFactory.getPageContext(this, request, response,null, true, 8192, true);
  23. application = pageContext.getServletContext();
  24. config = pageContext.getServletConfig();
  25. session = pageContext.getSession();
  26. out = pageContext.getOut();
  27. _jspx_out = out;
  28. //执行JSP中的JAVA代码
  29. ……
  30. //把JSP中的HTML代码输出到out对象中
  31. out.print(……);
  32. } catch (Throwable t) {
  33. out = _jspx_out;
  34. if (out != null && out.getBufferSize() != 0)
  35. out.clearBuffer();
  36. if (pageContext != null) pageContext.handlePageException(t);
  37. } finally {
  38. if (_jspxFactory != null) _jspxFactory.releasePageContext(pageContext);
  39. }
  40. }
  41. }



3 javabean
JavaBean必须具有不带参数的构造函数,即默认构造函数;不应该有公用(public)实例变量,所有实例变量应该通过setXXX和getXXX方法来进行操作


二 传统的设计方法及其弊端


一个基于J2EE的系统中的任何一个功能,在用户进行操作时可能都有三个步骤:用户发出一个请求,如通过一个按钮产生一个submit或是一个链接;接收用户的请求并进行处理;最后把处理结果返回给用户。“条条大路通罗马”,虽然要实现的功能都是这样,但通过不同的设计方法,我们可以产生不同的效果。在此,我做一个简单的归类,最后得出三种方法:
1) Servlet:把显示信息和处理用户请求的功能都放到Servlet中。这是运用J2EE开发B/S系统最原始的方法。
2) JSP:把显示信息和处理用户请求的功能都放到Servlet中,也许也会在某些地方运用到了Servlet,如验证用户权限。
3) JSP+Servlet+JavaBean:上面两种方法的弊端我在下面会进行分析,通过JSP+Servlet+JavaBean的模式,我们就可以达到“三权分立”,也就是在下面要讲到的MVC模式,从而为系统的开发和维护带来大大的好处。
在这一节中,我将实现一个简单的基于JSP的B/S实例,并通过这个实例分析传统的设计方法(前两种方法)的弊端。
1 一个简单的实例
举一个我们在网站上最常见到的例子:单击一个链接,然后显示出一个信息的列表。这样的例子随处可见,如一个论坛,当单击一个论坛版面就会显示出该版面的帖子。下面,我们就实现这样的一个实例。
首先做一个首页,代码如下:
Example1.jsp

  1. <%@ page contentType="text/html;charset=gb2312"%>
  2. "Content-Type" content="text/html; charset=gb2312">
  3. 实例首页
  4. "0" width="100%">


在首页中,我们只是通过一个简单的链接来跳到显示页。下面,我们看一下showallarticle.jsp有些什么:
Showallarticle.jsp

  1. <%@ page contentType="text/html;charset=gb2312"%>
  2. <%@ page import="java.util.*, java.lang.*,java.sql.*,javax.sql.*,org.dnxh.CommentItem"%>
  3. "Content-Type" content="text/html; charset=gb2312">
  4. 显示文章
  5. <%
  6. Connection conn=null;
  7. String con_user = "example1";
  8. String con_password = "example1";
  9. String con_dburl = "jdbc:oracle:thin:@localhost:iasdb";
  10. String con_driver = "oracle.jdbc.driver.OracleDriver";
  11. PreparedStatement pstmt=null;
  12. ResultSet rsComment=null;
  13. Vector vectorComment = new Vector();
  14. String selectSQL= "SELECT content, time\n" +
  15. "FROM article ORDER BY time DESC";
  16. try
  17. {
  18. DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
  19. Class.forName(con_driver);
  20. conn = DriverManager.getConnection(con_dburl,con_user,con_password);
  21. pstmt=conn.prepareStatement(selectSQL);
  22. rsComment=pstmt.executeQuery();
  23. while(rsComment.next())
  24. {
  25. CommentItem commentItem = new CommentItem();
  26. commentItem.setContent(rsComment.getString(1));
  27. commentItem.setTime(rsComment.getDate(2));
  28. vectorComment.add(commentItem);
  29. }
  30. vectorComment.trimToSize();
  31. }
  32. catch (Exception e){}
  33. %>
  34. "0" width="100%">
  35. <%
  36. if (vectorComment!=null && vectorComment.size()>0)
  37. {
  38. int counter=vectorComment.size();
  39. CommentItem commentlist = null;
  40. for (int i=0;i{
  41. commentlist=null;
  42. commentlist=(CommentItem)(vectorComment.get(i));
  43. %>
  44. <%
  45. }
  46. }
  47. %>
  48. 发表时间
  49. 文章内容
  50. <%=commentlist.getCmTime()%>
  51. <%=commentlist.getCmContent()%>

  52. 在这个JSP中,我们要用到一个简单的表article,它的只有两个字段(content varchar2(2000), time date)。另外,我们还用了个JavaBean来存放表中的每一条记录的值,这个JavaBean的代码:

    1. package org.dnxh;
    2. import java.util.Date;
    3. public class CommentItem
    4. {
    5. private String cmContent; //评论内容
    6. private java.sql.Date cmTime; //评论时间
    7. public CommentItem() {
    8. this.content="";
    9. this.time=null;
    10. }
    11. /**
    12. * 设置评论内容
    13. */
    14. public void setContent(String content)
    15. {this.content = content;};
    16. /**
    17. * 取得评论内容
    18. */
    19. public String getContent()
    20. {return content;};
    21. /**
    22. * 设置评论时间
    23. */
    24. public void setTime(java.sql.Date time)
    25. {this.time=ctime;};
    26. /**
    27. * 取得评论时间
    28. */
    29. public java.sql.Date getTime()
    30. {return time;};
    31. }

    下面是这个实例的运行结果:

    实例的首页

    显示文章信息
    2 这种设计方法的弊端
    上面的设计方法是许多人正在用的,特别是许多自学者(包括我的许多大学时的师弟师妹在内)。我在和他们进行讨论时,他们通常会觉得这种设计方法没什么不好的,但这是真的吗?现在,我就来分析一下上面的代码的弊端。
    1) 首先看一下代码(1),在首页中,我们是通过来跳到显示页的,也就是说,这个链接要跳到哪个页面都是在代码中写死的。对于现在这个实例这当然没问题,但是让我们假想一下,如果这是我们公司做的一个通用的行业系统,对于不同的客户,我们只需按客户的要求更改显示页面的风格而不用修改任何的程序时,我们要么就修改showallarticle.jsp这个页面中的HTML代码;要么重新编写一个JSP,并命名为showallarticleccustomer1.jsp,同时修改首页中的链接代码。当然,为了扩展,一般会选择后者,因为那样以后就可同时拥有两套界面的系统了。但是,每增加一种界面就要改一次页面中的链接,这对于一个拥有几十个页面的系统来说就已是一个不小的负担了。
    2) 再看一下代码(2),象这样的代码在所有要用到数据库的地方都会乃至,那就产生了大量的copy和paster操作,这样在程序进行修改时会带来大量的工作。如果一旦需要改变,如数据库地址换了,那么所有用到这些代码的页面都要进行修改,大大降低了系统的扩展性和复用性。
    3) 在showallarticle.jsp中,大量的Java代码和HTML混合在一起,使日后的维护工作难度加大。如果真要修改页面风格,而大部分美工人员是不懂得编程的(我们公司的页面就是由专门的美工来做的),那么要不懂编程的美工在一个参杂了大量JAVA代码的JSP中进行修改对他来说会是一件效率很低的事。
    4) 不利于分工。因为页面显示(要显示给用户的文章信息页面)、工作流程(当前页面要跳转到的下一页面)和业务逻辑(处理当前请求所要做的工作)都要在一个JSP中完成,那么一个开发人员就要同时完成显示页面、工作流程和业务逻辑的工作了。这对于一个大的系统来说是不可想象的。
    从上面可知,这种设计方法扩展性和复用性低、维护难、不利于开发过程中的分工合作,显然不是一种好的设计方法。正是因为这样,GOF出版了《设计模式》后,设计模式就受到了重视。那什么是设计模式?如何应用设计模式呢?在下一章将会进行介绍,并通过实例来进行讲解。