技术开发 频道

软件质量之路(4):建立核心框架

【IT168 技术文章】

    框架是一种特殊的软件,它为软件开发带来了高度的重用性,但同时它也需要高技巧的设计。软件组织使用框架来积累知识,对推动软件开发是一种有效的方式,但前提是你能够了解如何设计一个框架。

    什么是框架 

    在软件开发中出现过各种各样的框架,开源软件的兴起,使得各种各样的框架纷纷出现,例如,Apache组织下就拥有诸多的框架类产品,包括国内很熟悉的struts。那么,什么是框架呢?

    在设计模式中,Gamma等人为框架给出了一个定义:"框架就是一组协同工作的类,它们为特定类型的软件构筑了一个可重用的设计。"[Gamma 94,p.26]

    框架是针对特定的问题领域的,例如,struts是一个针对Web开发的框架。

    框架包括了一组的抽象概念。这些抽象概念来源于问题领域。例如,struts是基于MVC模式进行设计的,所以它必须为model、view、control建立抽象概念。

    框架使得这些抽象概念相互协作,并提供了一种扩展的形式,以实现重用。这是框架的具体工作。框架在抽象概念上进行工作,定义抽象概念之间的协作方式。框架和普通软件或是类库的区别就在于,用户通过扩展框架来重用该框架。这些扩展点的设计称为框架设计的核心。例如,在struts1.0中,采用的是类继承的扩展方式(对Action进行扩展)。

    框架实例

    spring(http://www.springframework.org)是一个开源的框架,这个框架定位于一个整体的基于J2EE的应用型框架。他的定位哲学是不重新发明轮子。例如,他提供了数据库访问机制,但是他的数据访问机制是基于JDBC、Hibernate和JDO的。重用和再包装的思路充满了整个spring框架。国内的软件产业大都定位在系统集成,这种思路非常适合于国内的软件组织。使用这个例子的目的也在于此。

    框架应该尽可能使用现有的技术。而不要重复投资。

    JDBC是一个非常基础的数据存取API。它封装了对关系型数据库的访问,但是应该承认,JDBC仍然属于层次较低的API,所以在使用的时候,我们不得不编写大量的代码,来完成一件简单的工作:

    PreparedStatement st = db.prepareStatement("SELECT USER.NAME FROM USER");
    ResultSet rs = st.executeQuery();
    while(rs.next()) {
         System.out.println(rs.getString(1));
    }
    rs.close();
    st.close();

    看到有多麻烦了吧,但是请注意,以上的代码存在严重的缺陷,因为没有任何处理异常的代码,加入异常代码意味着我们还需要增加一些代码,这种重复的劳动使得程序员的工作像是傻瓜一样。记得我们在代码自动化中的自动化原则吗?对于重复性的劳动,我们应该使其自动化。

    如何进行呢?注意,我们发现,除了SQL语句的不同以及记录集的处理不同以外,大部分的查询代码都没有太大的差别,所以我们的目标就是抽取共同的部分,而把特定的部分留给开发人员自己。

    那么,一个框架该做些什么呢?回忆框架的定义,框架就是定义一组的抽象体,及其抽象体之间的协作,并提供扩展。在JDBC中,抽象体有Datasource、Connection 、PreparedStatement、ResultSet、Statement、SQLException。所以,建立框架的第一步就是分析这些抽象体的行为,哪些是共同的,哪些是专有的。使用spring框架,最终的JDBC客户端代码是这样的:

    JdbcTemplate template = new JdbcTemplate(dataSource);
    final List names = new LinkedList();
    template.query("SELECT USER.NAME FROM USER",
     new RowCallbackHandler() {
      public void processRow(ResultSet rs) throws SQLException {
       names.add(rs.getString(1));
      }
     });

    首先,代码创建了一个JdbcTemplate实例,这是核心的JDBC的封装器,我们在后面可以看到它的部分实现。然后,通过给JdbcTemplate的query方法传入一个sql语句和一个回调的匿名类来完成填充names的操作。可以看到,客户端需要做的事情包括获得一个Datasource、提供一个sql Statement、以及一个具体的处理方法。这些动作每个客户端都不相同,但是对于Connection、PreparedStatement、Statement、SQLException的处理,基本上都是类似的。接下来我们就看看JdbcTemplate内部的做法:

  public void query(String sql, RowCallbackHandler callbackHandler) throws DataAccessException {
  Connection con = null;
  PreparedStatement ps = null;
  ResultSet rs = null;
  try {
   con = DataSourceUtils.getConnection(this.dataSource); 
   ps = con.prepareStatement(sql);
   rs = ps.executeQuery();

   while (rs.next()) {
    callbackHandler.processRow(rs);  
   }
   
   rs.close();
   ps.close();

  }
  catch (SQLException ex) {
   throw this.exceptionTranslater.translate("JdbcTemplate.query(sql)", sql, ex); 
  }
  finally {
   DataSourceUtils.closeConnectionIfNecessary(this.dataSource, con); 
  }
 }

 
    首先(1处),代码利用DataSourceUtils从dataSource中获得一个可用的连接,然后使用PrepareStatement的方式处理查询语句。2处是整段程序的精华,代码使用了一个回调接口,把从数据库中取出的记录集交给回调接口来处理。我们把这里的代码和前面的客户端代码结合起来看,就能够明白它的思路。3处用于处理异常,将SQLException转义为更具有意义的异常,即DataAccessException的子类,因为单单靠一个SQLException来表示复杂的数据库操作异常未免过于简单了。最后(4处),不管执行的结果如何,都关闭连接。

    应该说,这一段代码并不难理解。但它充分表现了框架的工作方式和以下我们将谈到的框架的意义。

0
相关文章