1.2 软件工程理论回顾
软件工程(SE,Software Engineering)是一门研究用工程化方法构建和维护有效的、实用的和高质量的软件学科。它涉及程序设计语言、数据库、软件开发工具、系统平台、标准、设计模式等方面。
在现代社会中,软件应用于多个方面。典型的软件有电子邮件、嵌入式系统、人机界面、办公套件、操作系统、编译器、数据库、游戏等。同时,各个行业几乎都有计算机软件的应用,如工业、农业、银行、航空、政府部门等。这些应用促进了经济和社会的发展,使得人们的工作更加高效,同时提高了生活质量。
软件工程师是对应用软件、创造软件的人们的统称,软件工程师按照所处的领域不同可以分为系统分析员、软件设计师、系统架构师、程序员、测试员等。人们也常常用程序员来泛指各种软件工程师。
软件工程(Software Engineering)的框架可概括为:目标、过程和原则。
1.2.1 软件工程概述
软件工程是一类工程,工程是将理论和知识应用于实践的科学。就软件工程而言,它借鉴了传统工程的原则和方法,以求高效地开发高质量软件。其中应用了计算机科学、数学和管理科学。计算机科学和数学用于构造模型与算法,工程科学用于制定规范、设计范型、评估成本及确定权衡,管理科学用于计划、资源、质量和成本的管理。
软件工程这一概念,主要是针对20世纪60年代“软件危机”而提出的。它首次出现在1968年NATO(北大西洋公约组织)会议上。自这一概念提出以来,围绕软件项目,开展了有关开发模型、方法及支持工具的研究。其主要成果有:提出了瀑布模型,开发了一些结构化程序设计语言(如PASCAL语言、Ada语言)、结构化方法等,并且围绕项目管理提出了费用估算、文档复审等方法和工具。综观20世纪60年代末至80年代初,其主要特征是,前期着重研究系统实现技术,后期开始强调开发管理和软件质量。
20世纪70年代初,自“软件工厂”这一概念提出以来,主要围绕软件过程及软件复用,开展了有关软件生产技术和软件生产管理的研究与实践。其主要成果有:提出了应用广泛的面向对象语言及相关的面向对象方法,大力开展了计算机辅助软件工程的研究与实践。尤其是近几年来,针对软件复用及软件生产,软件构件技术及软件质量控制技术、质量保证技术得到了广泛的应用。目前各个软件企业都十分重视资质认证,并想通过这些工作进行企业管理和技术的提升。
根据这一框架,可以看出,软件工程涉及软件工程的目标、软件工程原则和软件工程活动。
软件工程的主要目标是:生产具有正确性、可用性及开销合宜的产品。正确性意指软件产品达到预期功能的程度;可用性指软件基本结构、实现及文档为用户可用的程度;开销合宜性是指软件开发、运行的整个开销满足用户要求的程度。这些目标的实现不论在理论上还是在实践中均存在很多问题有待解决,它们形成了对过程、过程模型及工程方法选取的约束。
软件工程活动是“生产一个最终满足需求且达到工程目标的软件产品所需要的步骤”,主要包括需求、设计、实现、确认及支持等活动。需求活动包括问题分析和需求分析,问题分析获取需求定义,又称软件需求规约,需求分析生成功能规约。设计活动一般包括概要设计和详细设计。概要设计建立整个软件体系结构,包括子系统、模块及相关层次的说明、每一模块的接口定义。详细设计产生程序员可用的模块说明,包括每一模块中数据结构说明及加工描述。实现活动把设计结果转换为可执行的程序代码。确认活动贯穿于整个开发过程,实现完成后的确认,保证最终产品满足用户的要求。支持活动包括修改和完善。伴随以上活动,还有管理过程、支持过程、培训过程等。
软件工程围绕工程设计、工程支持及工程管理,提出了以下4项基本原则:
① 选取适宜开发范型。该原则与系统设计有关。在系统设计中,软件需求、硬件需求及其他因素之间是相互制约、相互影响的,经常需要权衡。因此,必须认识需求定义的易变性,采用适宜的开发范型予以控制,以保证软件产品满足用户的要求。
② 采用合适的设计方法。在软件设计中,通常要考虑软件的模块化、抽象与信息隐蔽、局部化、一致性及适应性等特征。合适的设计方法有助于这些特征的实现,以达到软件工程的目标。
③ 提供高质量的工程支持。“工欲善其事,必先利其器”。在软件工程中,软件工具与环境对软件过程的支持颇为重要。软件工程项目的质量与开销直接取决于对软件工程所提供的支撑质量和效用。
④ 重视开发过程的管理。软件工程的管理,直接影响可用资源的有效利用,生产满足目标的软件产品,提高软件组织的生产能力等问题。因此,仅当软件过程得以有效管理时,才能实现有效的软件工程。
这一软件工程框架说明,软件工程的目标是可用性、正确性和合算性;实施一个软件工程要选取适宜的开发范型,要采用合适的设计方法,要提供高质量的工程支撑,要实行开发过程的有效管理;软件工程活动主要包括需求、设计、实现、确认和支持等活动,每一活动可根据特定的软件工程,采用合适的开发范型、设计方法、支持过程及过程管理。根据软件工程这一框架,软件工程学科的研究内容主要包括:软件开发范型、软件开发方法、软件过程、软件工具、软件开发环境、计算机辅助软件工程(CASE)及软件经济学等。
自从软件工程概念提出以来,经过30多年的研究与实践,虽然“软件危机”没得到彻底解决,但在软件开发方法和技术方面已经有了很大的进步。尤其应该指出的是,自20 世纪80年代中期,美国工业界和政府部门开始认识到,在软件开发中,最关键的问题是软件开发组织不能很好地定义和管理其软件过程,从而使一些好的开发方法和技术都起不到所期望的作用。也就是说,在没有很好定义和管理软件过程的软件开发中,开发组织不可能在好的软件方法和工具中获益。
根据调查,中国的现状几乎和美国十多年前的情况一样,软件开发过程没有明确规定,文档不完整,也不规范,软件项目的成功往往归功于软件开发组的一些杰出个人或小组的努力。这种依赖于个别人员上的成功并不能为全组织的软件生产率和质量的提高奠定有效的基础,只有通过建立全组织的过程改善,采用严格的软件工程方法和管理,并且坚持不懈地付诸实践,才能取得全组织的软件过程能力的不断提高。
这一事实说明,只有坚持软件工程的4项基本原则,既重视软件技术的应用,又重视软件工程的支持和管理,并在实践中贯彻实施,才能高效地开发出高质量的软件。
1.2.2 软件工程的基本原理
自从1968 年提出“软件工程”这一术语以来,研究软件工程的专家学者们陆续提出了100多条关于软件工程的准则或信条。美国著名的软件工程专家Boehm综合这些专家的意见,并总结了TRW公司多年来开发软件的经验,于1983 年提出了软件工程的7 条基本原理。Boehm认为,这7条原理是确保软件产品质量和开发效率的原理的最小集合。它们是相互独立的,是缺一不可的最小集合;同时,它们又是相当完备的。人们当然不能用数学方法严格证明它们是一个完备的集合,但是可以证明,在此之前已经提出的100多条软件工程准则都可以由这7条原理的任意组合蕴涵或派生。
下面简要介绍软件工程的7条原理。
(1)用分阶段的生命周期计划严格管理
这一条是吸取前人的教训而提出来的。统计表明,50%以上的失败项目是由于计划不周而造成的。在软件开发与维护的漫长生命周期中,需要完成许多性质各异的工作。这条原理意味着,应该把软件生命周期分成若干阶段,并相应制订出切实可行的计划,然后严格按照计划对软件的开发和维护进行管理。Boehm认为,在整个软件生命周期中应指定并严格执行6类计划:项目概要计划、里程碑计划、项目控制计划、产品控制计划、验证计划、运行维护计划。
(2)坚持进行阶段评审
统计结果显示,大部分错误是在编码之前造成的,大约占63%。错误发现得越晚,改正它要付出的代价就越大,要差2~3个数量级。因此,软件的质量保证工作不能等到编码结束之后再进行,应坚持进行严格的阶段评审,以便尽早发现错误。
(3)实行严格的产品控制
开发人员最痛恨的事情之一就是改动需求。但是实践说明,需求的改动往往是不可避免的。这就要采用科学的产品控制技术来顺应这种要求,也就是要采用变动控制,又叫基准配置管理。当需求变动时,其他各个阶段的文档或代码随之相应变动,以保证软件的一致性。
(4)采纳现代程序设计技术
从20世纪60年代和70年代的结构化软件开发技术,到面向对象技术,从第一、第二代语言,到第四代语言,人们已经充分认识到:方法大于气力。采用先进的技术既可以提高软件开发的效率,又可以减少软件维护的成本。
(5)结果应能清楚地审查
软件是一种看不见、摸不着的逻辑产品。软件开发小组的工作进展情况可见性差,难于评价和管理。为更好地进行管理,应根据软件开发的总目标及完成期限,尽量明确地规定开发小组的责任和产品标准,从而使所得到的标准能清楚地审查。
(6)开发小组的人员应少而精
开发人员的素质和数量是影响软件质量和开发效率的重要因素,应该少而精。这一条基于两点原因:高素质开发人员的效率比低素质开发人员的效率要高几倍到几十倍,开发工作中犯的错误也要少得多;当开发小组为N人时,可能的通信信道为N(N-1)/2,可见随着人数N的增大,通信开销将急剧增大。
(7)承认不断改进软件工程实践的必要性
遵从上述6条基本原理,就能够较好地实现软件的工程化生产。但是,它们只是对现有的经验的总结和归纳,并不能保证赶上技术不断前进发展的步伐。因此,Boehm提出应把承认不断改进软件工程实践的必要性作为软件工程的第7条原理。根据这条原理,不仅要积极采纳新的软件开发技术,还要注意不断总结经验,收集进度和消耗等数据,进行出错类型和问题报告统计。这些数据既可以用来评估新的软件技术的效果,也可以用来指明必须着重注意的问题和应该优先进行研究的工具和技术。
面向方面的编程(AOP,Aspect Oriented Programming)被认为是近年来软件工程的另外一个重要发展。这里的方面指的是完成一个功能的对象和函数的集合。在这一方面相关的内容有泛型编程(Generic Programming)和模板。
1.2.3 软件工程的常用模型
软件工程的目标是提高软件的质量与生产率,最终实现软件的工业化生产。质量是软件需求方最关心的问题,生产率是软件供应方最关心的问题。质量与生产率之间有着内在的联系,高生产率必须以质量合格为前提。如果质量不合格,对供需双方都是坏事情。从短期效益看,追求高质量会延长软件开发时间并增大费用,似乎降低了生产率。从长期效益看,高质量将保证软件开发的全过程更加规范流畅,大大降低了软件的维护代价,实质上是提高了生产率,同时可获得很好的信誉。质量与生产率之间不存在根本的对立,好的软件工程方法可以同时提高质量与生产率。
软件工程的主要环节有:人员管理、项目管理、可行性与需求分析、系统设计、程序设计、测试、维护等,如图1-1所示。
图1-1 软件工程的主要环节
软件工程模型建议用一定的流程将各个环节连接起来,并可用规范的方式操作全过程常见的软件工程模型有:线性模型(图1-2)、渐增式模型(图1-3)、螺旋模型、快速原型模型、形式化描述模型等。
图1-3 软件工程的渐增式模型
最早出现的软件工程模型是线性模型(又称瀑布模型)。线性模型太理想化,已不再适合现代的软件开发模式。但应该认识到,“线性”是人们最容易掌握并能熟练应用的思想方法。当人们碰到一个复杂的“非线性”问题时,总是千方百计地将其分解或转化为一系列简单的线性问题,然后逐个解决。一个软件系统的整体可能是复杂的,而单个子程序总是简单的,可以用线性的方式来实现。线性是一种简洁,如果领会了线性的内涵,就不要再呆板地套用线性模型的外表,而应该用活它,例如,渐增式模型实质就是分段的线性模型,如图1-3所示。螺旋模型可以看做是接连的弯曲了的线性模型。在其他模型中都能够找到线性模型的影子。
套用固定的模型不是程序员的聪明之举。比如“程序设计”与“测试”之间的关系,习惯上总以为程序设计在先,测试在后,如图1-4(a)所示。而对于一些复杂的程序,将测试分为同步测试与总测试更有效,如图1-4(b)所示。
图1-4 设计和测试的关系
不论是什么软件工程模型,总是少不了图1-1中的各个环节。
1.2.4 软件体系结构和工具的选择
软件体系结构表示了一个软件系统的高层结构,主要特点有:① 软件系统结构是一个高层次上的抽象,它并不涉及具体的系统结构(如B/S还是C/S),也不关心具体的实现;② 软件体系结构必须支持系统所要求的功能,在设计软件体系结构的时候,必须考虑系统的动态行为;③ 在设计软件体系结构的时候,必须考虑有现有系统的兼容性、安全性和可靠性,同时还要考虑系统以后的扩展性和伸缩性,所以有时候必须在多个不同方向的目标中进行决策。
当前已经有一些关于规范化软件体系结构,例如,ISO的开放系统互联模型、X Window系统等。软件系统的结构通常被定义为两个部分:一个是计算部件;另一个就是部件之间的交互。如果把软件系统看成一幅图的话,计算部件就是其中的结点,而部件之间的交互就是结点之间的弧线。部件之间的连接可以被认为是一种连接器,如过程调用、事件广播、数据库查询等。正确的体系结构设计是软件系统成功的关键。
在理解了软件工程的重要性以后,没有相应的工具,也很难很好地完成一个系统。在需求分析和设计阶段,需要什么样的工具呢?当然最好是基于UML的CASE工具。当前比较流行的就是Rose,它是一个很好的分析和建立对象和对象关系的工具。在具体编码的时候,需要版本控制工具,MS的SourceSafe就是一个很好的版本管理工具和项目管理工具。另外需要注意的是,在软件工程过程中,需要建立文档编写的若干模板,以便开发人员按照这个模板编写规范的技术和说明文档。帮助文档可以用微软的HTML Help Workshop(hhw.exe)制作。最后,如果开发人员不是集中在一个地方的话,最好建立一个邮件列表,开发人员可以通过邮件系统讨论开发中的各项事宜。
1.2.5 软件开发方法综述
国外大的软件公司和机构一直在研究软件开发方法这个概念性的东西,而且也提出了很多实际的开发方法,如生命周期法、原型化方法、面向对象方法等。下面介绍几种流行的开发方法。
1.结构化方法
结构化开发方法是由E. Yourdon和L. L. Constantine提出的,即所谓的SASD方法,也可称为面向功能的软件开发方法或面向数据流的软件开发方法。结构化开发方法(Structured Method)是强调开发方法的结构合理性,以及所开发软件的结构合理性的软件开发方法。结构是指系统内各个组成要素之间的相互联系、相互作用的框架。结构化开发方法提出了一组提高软件结构合理性的准则,如分解与抽象、模块独立性、信息隐蔽等。针对软件生存周期各个不同的阶段,它有结构化分析(SA)、结构化设计(SD)和结构化程序设计(SP)等方法。
结构化分析方法给出一组帮助系统分析人员产生功能规约的原理与技术。它一般利用图形表达用户需求,使用的手段主要有数据流图、数据字典、结构化语言、判定表及判定树等。
结构化分析的步骤如下:
① 分析当前的情况,做出反映当前物理模型的DFD;
② 推导出等价的逻辑模型的DFD;
③ 设计新的逻辑系统,生成数据字典和基元描述;
④ 建立人机接口,提出可供选择的目标系统物理模型的DFD;
⑤ 确定各种方案的成本和风险等级,据此对各种方案进行分析;
⑥ 选择一种方案;
⑦ 建立完整的需求规约。
结构化设计方法给出一组帮助设计人员在模块层次上区分设计质量的原理与技术。它通常与结构化分析方法衔接起来使用,以数据流图为基础得到软件的模块结构。SD方法尤其适用于变换型结构和事务型结构的目标系统。在设计过程中,它从整个程序的结构出发,利用模块结构图表述程序模块之间的关系。
结构化设计的步骤如下:
① 评审和细化数据流图;
② 确定数据流图的类型;
③ 把数据流图映射到软件模块结构,设计出模块结构的上层;
④ 基于数据流图逐步分解高层模块,设计中、下层模块;
⑤ 对模块结构进行优化,得到更为合理的软件结构;
⑥ 描述模块接口。
结构化设计方法有以下的一些设计原则:
① 使每个模块执行一个功能(坚持功能性内聚);
② 每个模块用过程语句(或函数方式等)调用其他模块;
③ 模块间传送的参数做数据用;
④ 模块间共用的信息(如参数等)尽量少。
2.面向数据结构的软件开发方法
1975年,M. A. Jackson提出了一类至今仍广泛使用的软件开发方法。这一方法从目标系统的输入/输出数据结构入手,导出程序框架结构,再补充其他细节,就可得到完整的程序结构图。这一方法对输入/输出数据结构明确的中小型系统特别有效,如商业应用中的文件表格处理。该方法也可与其他方法结合,用于模块的详细设计。
Jackson方法也称为面向数据结构的软件设计方法。
一般通过以下5个步骤来完成设计。
① 分析并确定输入数据和输出数据的逻辑结构,并用Jackson结构图来表示这些数据结构。
② 找出输入数据结构和输出数据结构中有对应关系的数据单元。
③ 按以下的规则由输入/输出的数据结构导出程序结构。
为每一对在输入数据结构和输出数据结构中有对应关系的单元画一个处理框。
为输入和输出数据结构中剩余的数据单元画一个处理框。
所有处理框在程序结构图上的位置,应与由它处理的数据单元在数据结构Jackson图上的位置一致。
必要时,可以对映射导出的程序结构图进行进一步的细化。
④ 列出基本操作与条件,并把它们分配到程序结构图的适当位置。
⑤ 用伪码写出程序。
3.面向问题的分析法
PAM(Problem Analysis Method)是20世纪80年代末由日立公司提出的一种软件开发方法。PAM方法希望能兼顾Yourdon方法、Jackson方法和自底向上的软件开发方法的优点,而避免它们的缺陷。它的基本思想是:考虑到输入/输出数据结构,指导系统的分解,在系统分析指导下逐步综合。这一方法的具体步骤是:从输入/输出数据结构导出基本处理框;分析这些处理框之间的先后关系;按先后关系逐步综合处理框,直到画出整个系统的PAD图。从上述步骤中可以看出,这一方法本质上是综合的自底向上的方法,但在逐步综合之前已进行了有目的的分解,这个目的就是充分考虑系统的输入/输出数据结构。
PAM方法的另一个优点是使用PAD图。这是一种二维树形结构图,是到目前为止最好的详细设计表示方法之一,远远优于NS图和PDL语言。
这一方法在日本较为流行,软件开发的成功率也很高。由于在输入/输出数据结构与整个系统之间同样存在着鸿沟,这一方法仍只适用于中小型问题。
4.原型化方法
产生原型化方法的原因很多,主要随着系统开发经验的增多,同时也发现并非所有的需求都能够预先定义,而且反复修改是不可避免的。当然能够采用原型化方法是因为开发工具的快速发展,可以迅速的开发出一个可以让用户看的见、摸的着的系统框架,这样,对于计算机不是很熟悉的用户就可以根据这个样板提出自己的需求。
开发原型化系统一般由以下几个阶段:
① 确定用户需求;
② 开发原始模型;
③ 征求用户对初始原型的改进意见;
④ 修改原型。
原型化开发方法比较适合于用户需求不清、业务理论不确定、需求经常变化的情况。当系统规模不是很大也不太复杂时采用该方法是比较好的。
5.面向对象的软件开发方法
当前计算机业界最流行的几个术语就是分布式、并行和面向对象。由此可以看到面向对象这个概念在当前计算机业界的地位。如当前流行的两大面向对象技术DCOM和CORBA就是例子。当然实际用到的还是面向对象的编程语言,如C++。不可否认,面向对象技术是软件技术的一次革命,在软件开发史上具有里程碑的意义。
随着OOP(面向对象编程)向OOD(面向对象设计)和OOA(面向对象分析)的发展,最终形成面向对象的软件开发方法OMT(Object Modeling Technique)。这是一种自底向上和自顶向下相结合的方法,而且它以对象建模为基础,从而不仅考虑了输入/输出数据结构,实际上也包含了所有对象的数据结构。所以OMT彻底实现了PAM没有完全实现的目标。不仅如此,OO技术在需求分析、可维护性和可靠性这3 个软件开发的关键环节和质量指标上有了实质性的突破,基本地解决了在这些方面存在的严重问题。
面向对象系统采用了自底向上的归纳、自顶向下的分解方法,它通过对对象模型的建立,能够真正建立基于用户的需求,而且系统的可维护性大大改善。当前业界关于面向对象建模的标准是UML(Unified Modeling Language)。
6.可视化开发方法
其实可视化开发并不能单独地作为一种开发方法,更加贴切地说,可以认为它是一种辅助工具,如用过Sybase的S-Design的人都知道,用这个工具可以进行显示的图形化的数据库模式的建立,并可以导入到不同的数据库中去。用过VB,Delphi,C++ Builder等开发工具的人一定不少,实际上就是在使用可视化开发工具。
当然,不可否认的是,这些开发工具只是在编程这个环节上用了可视化,而不是在系统分析和系统设计这个高层次上用了可视化的方法。实际上,建立系统分析和系统设计的可视化工具是一个很好的卖点,国外有很多工具都致力于这方面产品的设计。如Business Object就是一个非常好的数据库可视化分析工具。
可视化开发使人们把注意力集中在业务逻辑和业务流程上,用户界面可以用可视化工具方便地构成。通过操作界面元素,如菜单、按钮、对话框、编辑框、单选框、复选框、列表框和滚动条等,由可视开发工具自动生成应用软件。
1.2.6 软件开发的发展方向
有一点可以达成共识的就是,如果一个像Windows这样的操作系统,不进行全面的规划,不采用软件工程的思想和方法,是绝对开发不出来的。
Windows的成功不在于它在进程管理和调度,文件系统、内存管理、界面设计等方面有多少成功的创新,它成功最大的一点就是把所有的技术能够合理地整合起来,并集中到一个Windows操作系统特有的框架结构中去。
更为重要的是,Windows的每一项技术创新都能够有效的整合到Windows框架中去,如COM,XML等技术,通过ActiveX,DCOM等技术使Windows从桌面操作系统发展成为一个基于网络的操作系统。
从微软产品系列可以看到软件工程的作用,微软的所有产品都有一个整体的框架结构,如Office软件,通过OLE技术进行有效的通信和联系。如Visual系列开发工具,提供了相似的开发界面使用户学会一种开发工具以后能够很容易的学习其他的开发工具。如SQL Server和Access,尽管它们适用的范围不同,但是它们表现给用户的界面,特别是在查询和分析上表现了高度的一致性。
更值得一提的是,因为设计结构的合理性,因为在开发前期做了很多分析和调研,考虑了扩展性和伸缩性,微软的系列产品能够很快地利用新的技术并采用统一的结构形式表现出来。如当网络成为计算机发展的主流的时候,几乎微软所有的工具都能够快速地支持基于网络的开发和应用。
相比之下,我们国内很多公司的产品很少具有连续性,往往是新的一个产品完全重起炉灶,和老的产品没有太大关系。这就是在设计产品的时候,没有很好地进行抽象和概念、逻辑设计,造成的结果是从旧的产品中提取不出一些有用的、共性的东西为后来的产品所使用。
当然,很多开发人员从心里也承认一个大的系统确实需要软件工程的依托,但是一个小的工程项目是否就可以仓促上马呢?答案是否定的。所谓麻雀虽小,五脏俱全。无论是大项目、还是小项目,它们作为一个项目,都需要有一个需求分析、系统结构建立、设计、编码、测试等阶段。这是任何一个项目都不可缺少的。
往往可以看到很多大公司的IT部门的人员都在不停地做各种各样的报表,当各个部门提出一种新类型的报表的时候,就从数据库中提取相应的数据并画出业务人员所需要的样式结构,很少是提供了一个通用的模板,当然提供高层API接口进行这种操作地就更少了。这样不可避免地使开发人员陷入一些琐碎的报表编制工作。而造成这个局面的很重要的一个原因就是没有在系统开发的前期进行很好的调研、需求分析和系统体系结构的设计。
“敏捷开发”(Agile Development)被认为是软件工程的一个重要的发展。它强调软件开发应当是能够对未来可能出现的变化和不确定性做出全面反应的。
敏捷开发被认为是一种“轻量级”的方法。在轻量级方法中最负盛名的应该是“极限编程”(XP,Extreme Programming)。而与轻量级方法相对应的是“重量级方法”的存在。重量级方法强调以开发过程为中心,而不是以人为中心。重量级方法的例子如CMM/PSP/TSP。