`

软件质量之路之四:建立核心框架

阅读更多

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

 

什么是框架

在软件开发中出现过各种各样的框架,开源软件的兴起,使得各种各样的框架纷纷出现,例如,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); (1) 
      ps = con.prepareStatement(sql);
      rs = ps.executeQuery();
    
    while (rs.next()) {
      callbackHandler.processRow(rs);  (2)
    }
            
    rs.close();
    ps.close();

  }catch (SQLException ex) {
    throw this.exceptionTranslater.translate("JdbcTemplate.query(sql)", sql, ex); (3)

    }finally {
       DataSourceUtils.closeConnectionIfNecessary(this.dataSource, con); (4)
  }
}

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

 

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

 

框架对软件开发的意义

 

知识积累

框架的核心价值是对知识的积累。软件开发是一项知识性的活动。但是知识存在于人的大脑中,是最难进行积累的。而在软件开发中,代码是最确定的知识,人和机器通过浏览代码都能够了解代码的目的,而且不会出现不同的理解。所以,从代码出发进行知识的积累是最佳的办法。框架就是这种思路的产出物。框架包含了大量的代码,这些代码是对某个特定问题领域中抽象概念及这些抽象概念之间关系的描述。所以,框架能够胜任知识积累的工作。

 

虽然代码是框架的核心,但是光有代码的框架是很难为人所理解的。代码的层次太低,开发人员从代码的角度来完全的理解框架是很困难的。所以,必须要有层次高于代码的工件。这些工件可以是设计文档、领域模型、UML图、JavaDoc。他们的目的都是为了帮助框架的开发人员和使用人员顺利的理解框架。

 

就像我们上面的实例中,通过一个JdbcTemplate对象,就将一个JDBC的最佳实践给积累起来了。当然,你也可以使用文档要求开发人员按照某种方式来使用JDBC,但在实践中会遇到不少的问题,例如,JDBC的异常处理可能过于繁琐而被忽略。虽然大多数时候都不会有问题,但是当问题发生时往往会很麻烦。采用框架的方式则不会有类似的问题。

 

资产的保护

知识积累本身就是一项对资产的保护工作。而另一项很重要的保护工作就是软件组织(尤其是企业)需要保证对知识的学习和改进是经过合法授权的。例如,知识的非法外流是任何组织都不希望看到的。将知识积累为框架的形式有助于缓解这种情况。框架可以是以源码形式发布的,也可以是以库形式发布的,为不同的框架用户选择不同的发布形式,可以起到权限控制的作用。

 

鼓励重用

框架之所以称为框架,是因为它可以重用。在软件组织中形成以框架为核心的开发方式,在开发中使用框架,并在开发完成后改进框架。在这个反覆的过程中,重用的工作就已经开展起来了。

 

重用其实并没有那么困难。就像上面的例子中,其实代码并不难,思路也很清晰,其实就是将通用的行为抽取出来。

 

优化架构

框架代表了一种优秀的软件架构。框架定义了扩展方式,从而规范了框架的使用行为。这使得软件能够保持整体架构的稳定性和一致性。

 

在上面的例子中,使用框架之后,客户端可以节省大量的代码,代码结构会更加清晰。

 

大规模软件设计

大规模的软件设计的关键在于对应用进行合理的划分,并提供一种一致的方式建立架构。大规模的软件设计要求核心的设计人员工作在一个抽象的层次上。虽然他们属于设计人员,但是同样需要编写代码,而这些代码则是框架代码。

 

在敏捷方法中,设计师的职业决不意味着你仅仅只需要编写设计文档,如果你常常阅读一些规范的话,你会发现,很多的规范是采用代码编写而成的。只不过这些代码并不提供实现,只提供了抽象接口。

 

如何进行有效的框架设计

一个好的框架设计是有一定的准则可供遵循的。以下给出的一些概念奠定了框架开发的理论基础。

 

设计抽象层次。

在框架的定义中,抽象体是至关重要的。抽象体的定义取决于框架的目标。没有目标的框架决不是一个框架,要么是一个类库,要么是一种编程语言。在上面的例子中,首先是有了一个简化JDBC操作的目标,然后从这个目标出发定义抽象体。于是我们得到了Connection 、ResultSet、Statement等抽象体。

 

在抽象层次中规范行为。光有抽象体还是没有办法工作。还需要定义出抽象体的行为。在上例中,我们定义了获取数据集的行为。但是在JDBC中,除了获取数据集,可能还需要将数据填充到值对象中,还需要能够支持CRUD的所有操作。这些都是抽象的行为。有了这些行为之后,我们就需要规范、穷尽这些行为。

 

分析抽象行为的通用部分和非通用部分。在抽象体的行为中,有些动作是通用的,有些是特殊的。前者就是框架要实现的部分。而后者则作为扩展留给用户。

 

将抽象层次提取为框架,并设计扩展点。有了抽象体、抽象体的通用行为之后,就可以设计扩展点了。最简单的扩展点是采用方法调用的方式,复杂的可能通过设计模式或是配置文件等方式。扩展点设计优劣的评价标准是使用起来是否方便,这里的使用包括应用、调试、测试等。

 

适当的使用设计模式。设计模式代表了先进的软件设计思路。在框架中适当的使用设计模式有助于改进框架的结构。例如在上例中,扩展点的设计就采用了回调的设计模式。在框架设计中不宜采用过多的设计模式,这会使得框架理解起来困难。当然也有反例。

 

反例-Junit Framework。作为自动化单元测试框架,Junit在简小的设计中采用了大量的设计模式,包括Command模式、Template Method模式、Collecting Parameter模式、Pluggable Selector模式、Adapter模式、Composite模式等。

 

简化框架的使用。在上例中,框架的扩展点设计完毕后,使用框架的代码仍然比较复杂,回调接口和匿名类仍然会把人弄的有些莫名其妙。所以,为了使框架使用方便,一定程度的简化还是需要的。

 

在Junit框架中,我们只需要让测试方法以test开头就能够自动进行测试,而这种功能是Command模式无法提供的。按照Command模式的要求,一个测试类只能包括一个测试方法。原因就在于Junit中利用了Pluggable Selector模式、Adapter模式、以及反射技术对Command模式作了新的封装:

protected void runTest() throws Throwable { 
  Method runMethod= null; 
  try { 
    runMethod= getClass().getMethod(fName, new Class[0]); 
  } catch (NoSuchMethodException e) { 
    assert("Method """+fName+""" not found", false); 
  } 
  
  try { 
    runMethod.invoke(this, new Class[0]); 
  } 

}

 

 

隔离第三方技术

当前的软件开发向着协作的方向发展。在这种情况下,大量的第三方软件出现了。软件业的分工将会给软件业带来繁荣,但是对于软件组织来说,就需要考虑第三方软件的成本、生命力、本组织系统对其的依赖程度等问题。这部分的工作应该交给框架。让框架来负责把核心应用和第三方技术隔离开来。例如,作为企业应用的开发者,我发现在数据库层次上的变化实在是太大了,新的xquery查询语言、ORM技术,这些都使得应用系统需要不断的变化。这无疑给应用系统增加了风险。因此,我决定设计一个抽象的层次,把这些技术和核心应用隔离起来。抽象层次只负责向数据库询问符合某种条件的数据,至于这个查询采用sql还是 xquery来处理那都没有关系。因此技术细节被隔离了。

 

另一个实例

上文讨论的例子是一个简单的例子,主要是从代码层面来讲述框架的概念。但是为了完全理解框架的威力,我们需要从更高的层次来看待框架的概念。我们选择的例子是Eclipse。从IBM向开放源码界捐赠Eclipse以来,Eclipse迅速成为一种非常优秀的集成开发工具(它绝对不是简单的Java IDE),Eclipse缘何成功呢?关键在于Eclipse的设计理念和根据设计理念发展起来的底层支持框架。

 

在软件开发中,需要各种各样的技能,需要各种各样的开发工具,但是工具之间如果彼此不能够相互交互,那么开发流程就难以连贯。Eclipse的目标就是要解决这个问题。

 

Eclipse平台的价值在于它的促进作用:根据插件模型来快速开发集成功能部件。

那么怎么做呢?我们来看Eclipse自己对这个问题的描述: Eclipse 的核心是动态发现插件的体系结构。平台负责处理基本环境的后台工作,并提供标准的用户导航模型。于是每个插件可以专注于执行少量的任务。有哪些类型的任务?定义、测试、制作动画、发布、编译、调试、图解等等,只要您想象得到的应有尽有。


Eclipse的平台运行环境是框架的支持部分,而平台中涉及的Workbench、Help、Team、Workspace都属于抽象体,而插头表示系统的扩展点(称为hook)。

 

这里重点要讨论的是Eclipse的定位哲学。Eclipse作为一个集成开发工具,他希望能够将各种各样的开发工具集中到一个平台下,但是所有的事情都由一个组织来做是不可能的,最好的方式就是建立一种组织方式,能够将不同技术提供商的技术集成起来。所以他的定位哲学就是提供做事的方法,而不做具体的事情。

 

这种思路在优秀的软件设计中是很常见的,另一个优秀的范例是Ant。由于篇幅所限,我们就不进行深入讨论了。

所以Eclipse只是提供针对IDE环境、Java对象模型提供了一组的抽象体,插件的开发者可以根据自己的需要,设计自己的插件,通过扩展点接入到Eclipse。Eclipse是一个成功的软件,也是一个成功的框架。

 

深入了解

developerWorks 上的 Eclipse 平台入门一文提供了Eclipse的相关信息。

框架过程模式是一本讨论框架设计过程的书籍,您可以从中了解到框架的开发过程和普通软件的开发过程的区别。

 

关于作者
林星,致力于研究敏捷理论和优秀的软件设计思想,并将之应用于国内的软件组织。可以通过 iamlinx@21cn.com和他联系,也可以通过访问 www.qca.cn 来获得更多的信息。 
 

分享到:
评论

相关推荐

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

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

    软件工程导论课件(含课件与复习要求)

    《软件工程导论》围绕软件工程最基本的核心——“质量和生产率”展开,力求通过一些具体的实例来说明软件工程的思想和方法。《软件工程导论》在回答为什么要研究软件工程问题之后,首先系统地给出软件工程的知识体系...

    软件项目生存期模型-软件项目管理

    规划阶段是软件项目计划的编制阶段,它的主要任务是确定项目的时间、成本、资源和质量目标,并建立详细的项目计划和管理计划。 3. 执行: 执行阶段是软件项目的核心阶段,它的任务是根据项目计划完成项目工作,并...

    软件工程-13-软件过程管理与改进.pptx

    软件过程管理 承认并坚持过程改进带来的潜在积极作用,是质量管理的核心任务。 擅于站在巨人的肩膀上,在别人的工作基础上进行过程的利用和改进。 由于项目领域的多样性以及边界约束的复杂性,一个一成不变的通用...

    软件设计规范

    这是因为软件之软,也是因为硬件平台的多样性和特殊性。 完整把握,从头设计是第一原则。因为软件世界自己并不能统一,还有继续分化的趋势。没有根本一致的基础可能是软件的本性。退回到一无所有是处理软件问题的...

    软件工程应用题.txt

    答:软件工程是以质量为核心,为了经济地开发满足客户需求的软件而研究、建立和应用的系统化的、有规则 的、可度量的和可控制的工程原则、方法,涉及软件过程、项目管理、开发工具,甚至企业文化等各个方面。...

    talflow(c语言代码自动生成工具) 1.4.0

    talmodeler是由功能需求建模、流程图建模、基于模型仿真、基于模型测试等相关工具共同组成的基于模型的软件全过程开发环境,各个工具都建立在共同的数据存储模型之上,在相同的底层组件之上采用相同的技术框架,在...

    talflow1.3.0(c语言代码自动生成工具)

    talmodeler是由功能需求建模、流程图建模、基于模型仿真、基于模型测试等相关工具共同组成的基于模型的软件全过程开发环境,各个工具都建立在共同的数据存储模型之上,在相同的底层组件之上采用相同的技术框架,在...

    talflow1.1.5(c代码自动生成工具)

    talmodeler是由功能需求建模、流程图建模、基于模型仿真、基于模型测试等相关工具共同组成的基于模型的软件全过程开发环境,各个工具都建立在共同的数据存储模型之上,在相同的底层组件之上采用相同的技术框架,在...

    talflow1.1.0(c代码生成工具)

    talmodeler是由功能需求建模、流程图建模、基于模型仿真、基于模型测试等相关工具共同组成的基于模型的软件全过程开发环境,各个工具都建立在共同的数据存储模型之上,在相同的底层组件之上采用相同的技术框架,在...

    talflow1.2.0(c代码自动生成工具)

    talmodeler是由功能需求建模、流程图建模、基于模型仿真、基于模型测试等相关工具共同组成的基于模型的软件全过程开发环境,各个工具都建立在共同的数据存储模型之上,在相同的底层组件之上采用相同的技术框架,在...

    talflow1.1.8(c代码自动生成工具)

    talmodeler是由功能需求建模、流程图建模、基于模型仿真、基于模型测试等相关工具共同组成的基于模型的软件全过程开发环境,各个工具都建立在共同的数据存储模型之上,在相同的底层组件之上采用相同的技术框架,在...

    iBizPMS软件生产管理系统-其他

    核心框架:Spring Boot 持久层框架: Mybatis-plus 服务发现:Nacos 日志管理:Logback 项目管理框架: Maven 前端技术架构: iBiz-Vue-R7 前端框架:vue.js 路由:vue-router 状态管理:vuex 国际化:vue-i18n 数据...

    红帽原厂DevOps战略计划分析1.5版

    建立DevOps框架体系,以管理为基础,技术为主线的方式,关注于自动化和指标的提升,从人、物件、流程、指标、规范等方面落实,全面打造全协同的工作链体系,促进开发(应用程序/软件工程)、技术运营和质量保障(QA)...

    ArchSummit深圳 2019年全球架构师峰会PPT合集(59份).zip

    微服务框架及治理系统演进之路 微服务架构演进之路 微服务架构实践 图数据库GDB的设计与实践 如何通过结对编程进行高质量的软件开发 如何利用混沌工程打造健壮的分布式系统 容器化场景下的性能优化实践 七步构建跨越...

    新基建数据中心规划设计方案.docx

    软件环境:部署云计算平台、大数据处理框架、虚拟化软件等必要的软件环境,为应用开发提供基础支持。 系统部署:根据数据中心的架构和功能需求,进行系统的部署和配置,确保各项功能的正常运行。 运维管理:建立完善...

    高级软件架构师复习提纲

    25、微软运营框架将配置管理定义为一个非常重要的过程,关于“配置管理”说法正确的是:实现再现或回退到前一版本的配置/需要团队在数据库或其他工具中记录各种配置 /对解决方案各个版本的历史记录进行追溯和控制的...

    magicware Linux 正式版

    Magiclayer中间件产品提供业务运行框架,可以基于此框架快速的进行业务模块的开发,加快开发周期,提高开发质量: 中间件产品magiclayer的主要特性如下: 高度可配置的框架 通过配置文件的设计,中间件框架提供...

    javaEE_SSH土地档案管理系统设计软件源码+数据库+WORD毕业论文文档.zip

    其中,土地管理的许多业务工作,如,动态监测、建设用地管理、土地监察、地价评估都必须建立在地籍、土地详查系统的基础之上,或者说与其有着千丝万缕的联系。因此,土地信息系统的核心问题是建立地籍管理信息系统和...

Global site tag (gtag.js) - Google Analytics